1045 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * sysfs.c - ACPI sysfs interface to userspace.4 */5 6#define pr_fmt(fmt) "ACPI: " fmt7 8#include <linux/acpi.h>9#include <linux/bitmap.h>10#include <linux/init.h>11#include <linux/kernel.h>12#include <linux/kstrtox.h>13#include <linux/moduleparam.h>14 15#include "internal.h"16 17#ifdef CONFIG_ACPI_DEBUG18/*19 * ACPI debug sysfs I/F, including:20 * /sys/modules/acpi/parameters/debug_layer21 * /sys/modules/acpi/parameters/debug_level22 * /sys/modules/acpi/parameters/trace_method_name23 * /sys/modules/acpi/parameters/trace_state24 * /sys/modules/acpi/parameters/trace_debug_layer25 * /sys/modules/acpi/parameters/trace_debug_level26 */27 28struct acpi_dlayer {29 const char *name;30 unsigned long value;31};32struct acpi_dlevel {33 const char *name;34 unsigned long value;35};36#define ACPI_DEBUG_INIT(v) { .name = #v, .value = v }37 38static const struct acpi_dlayer acpi_debug_layers[] = {39 ACPI_DEBUG_INIT(ACPI_UTILITIES),40 ACPI_DEBUG_INIT(ACPI_HARDWARE),41 ACPI_DEBUG_INIT(ACPI_EVENTS),42 ACPI_DEBUG_INIT(ACPI_TABLES),43 ACPI_DEBUG_INIT(ACPI_NAMESPACE),44 ACPI_DEBUG_INIT(ACPI_PARSER),45 ACPI_DEBUG_INIT(ACPI_DISPATCHER),46 ACPI_DEBUG_INIT(ACPI_EXECUTER),47 ACPI_DEBUG_INIT(ACPI_RESOURCES),48 ACPI_DEBUG_INIT(ACPI_CA_DEBUGGER),49 ACPI_DEBUG_INIT(ACPI_OS_SERVICES),50 ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER),51 ACPI_DEBUG_INIT(ACPI_COMPILER),52 ACPI_DEBUG_INIT(ACPI_TOOLS),53};54 55static const struct acpi_dlevel acpi_debug_levels[] = {56 ACPI_DEBUG_INIT(ACPI_LV_INIT),57 ACPI_DEBUG_INIT(ACPI_LV_DEBUG_OBJECT),58 ACPI_DEBUG_INIT(ACPI_LV_INFO),59 ACPI_DEBUG_INIT(ACPI_LV_REPAIR),60 ACPI_DEBUG_INIT(ACPI_LV_TRACE_POINT),61 62 ACPI_DEBUG_INIT(ACPI_LV_INIT_NAMES),63 ACPI_DEBUG_INIT(ACPI_LV_PARSE),64 ACPI_DEBUG_INIT(ACPI_LV_LOAD),65 ACPI_DEBUG_INIT(ACPI_LV_DISPATCH),66 ACPI_DEBUG_INIT(ACPI_LV_EXEC),67 ACPI_DEBUG_INIT(ACPI_LV_NAMES),68 ACPI_DEBUG_INIT(ACPI_LV_OPREGION),69 ACPI_DEBUG_INIT(ACPI_LV_BFIELD),70 ACPI_DEBUG_INIT(ACPI_LV_TABLES),71 ACPI_DEBUG_INIT(ACPI_LV_VALUES),72 ACPI_DEBUG_INIT(ACPI_LV_OBJECTS),73 ACPI_DEBUG_INIT(ACPI_LV_RESOURCES),74 ACPI_DEBUG_INIT(ACPI_LV_USER_REQUESTS),75 ACPI_DEBUG_INIT(ACPI_LV_PACKAGE),76 77 ACPI_DEBUG_INIT(ACPI_LV_ALLOCATIONS),78 ACPI_DEBUG_INIT(ACPI_LV_FUNCTIONS),79 ACPI_DEBUG_INIT(ACPI_LV_OPTIMIZATIONS),80 81 ACPI_DEBUG_INIT(ACPI_LV_MUTEX),82 ACPI_DEBUG_INIT(ACPI_LV_THREADS),83 ACPI_DEBUG_INIT(ACPI_LV_IO),84 ACPI_DEBUG_INIT(ACPI_LV_INTERRUPTS),85 86 ACPI_DEBUG_INIT(ACPI_LV_AML_DISASSEMBLE),87 ACPI_DEBUG_INIT(ACPI_LV_VERBOSE_INFO),88 ACPI_DEBUG_INIT(ACPI_LV_FULL_TABLES),89 ACPI_DEBUG_INIT(ACPI_LV_EVENTS),90};91 92static int param_get_debug_layer(char *buffer, const struct kernel_param *kp)93{94 int result = 0;95 int i;96 97 result = sprintf(buffer, "%-25s\tHex SET\n", "Description");98 99 for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) {100 result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",101 acpi_debug_layers[i].name,102 acpi_debug_layers[i].value,103 (acpi_dbg_layer & acpi_debug_layers[i].value)104 ? '*' : ' ');105 }106 result +=107 sprintf(buffer + result, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS",108 ACPI_ALL_DRIVERS,109 (acpi_dbg_layer & ACPI_ALL_DRIVERS) ==110 ACPI_ALL_DRIVERS ? '*' : (acpi_dbg_layer & ACPI_ALL_DRIVERS)111 == 0 ? ' ' : '-');112 result +=113 sprintf(buffer + result,114 "--\ndebug_layer = 0x%08X ( * = enabled)\n",115 acpi_dbg_layer);116 117 return result;118}119 120static int param_get_debug_level(char *buffer, const struct kernel_param *kp)121{122 int result = 0;123 int i;124 125 result = sprintf(buffer, "%-25s\tHex SET\n", "Description");126 127 for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) {128 result += sprintf(buffer + result, "%-25s\t0x%08lX [%c]\n",129 acpi_debug_levels[i].name,130 acpi_debug_levels[i].value,131 (acpi_dbg_level & acpi_debug_levels[i].value)132 ? '*' : ' ');133 }134 result +=135 sprintf(buffer + result, "--\ndebug_level = 0x%08X (* = enabled)\n",136 acpi_dbg_level);137 138 return result;139}140 141static const struct kernel_param_ops param_ops_debug_layer = {142 .set = param_set_uint,143 .get = param_get_debug_layer,144};145 146static const struct kernel_param_ops param_ops_debug_level = {147 .set = param_set_uint,148 .get = param_get_debug_level,149};150 151module_param_cb(debug_layer, ¶m_ops_debug_layer, &acpi_dbg_layer, 0644);152module_param_cb(debug_level, ¶m_ops_debug_level, &acpi_dbg_level, 0644);153 154static char trace_method_name[1024];155 156static int param_set_trace_method_name(const char *val,157 const struct kernel_param *kp)158{159 u32 saved_flags = 0;160 bool is_abs_path = true;161 162 if (*val != '\\')163 is_abs_path = false;164 165 if ((is_abs_path && strlen(val) > 1023) ||166 (!is_abs_path && strlen(val) > 1022)) {167 pr_err("%s: string parameter too long\n", kp->name);168 return -ENOSPC;169 }170 171 /*172 * It's not safe to update acpi_gbl_trace_method_name without173 * having the tracer stopped, so we save the original tracer174 * state and disable it.175 */176 saved_flags = acpi_gbl_trace_flags;177 (void)acpi_debug_trace(NULL,178 acpi_gbl_trace_dbg_level,179 acpi_gbl_trace_dbg_layer,180 0);181 182 /* This is a hack. We can't kmalloc in early boot. */183 if (is_abs_path)184 strcpy(trace_method_name, val);185 else {186 trace_method_name[0] = '\\';187 strcpy(trace_method_name+1, val);188 }189 190 /* Restore the original tracer state */191 (void)acpi_debug_trace(trace_method_name,192 acpi_gbl_trace_dbg_level,193 acpi_gbl_trace_dbg_layer,194 saved_flags);195 196 return 0;197}198 199static int param_get_trace_method_name(char *buffer, const struct kernel_param *kp)200{201 return sysfs_emit(buffer, "%s\n", acpi_gbl_trace_method_name);202}203 204static const struct kernel_param_ops param_ops_trace_method = {205 .set = param_set_trace_method_name,206 .get = param_get_trace_method_name,207};208 209static const struct kernel_param_ops param_ops_trace_attrib = {210 .set = param_set_uint,211 .get = param_get_uint,212};213 214module_param_cb(trace_method_name, ¶m_ops_trace_method, &trace_method_name, 0644);215module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644);216module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644);217 218static int param_set_trace_state(const char *val,219 const struct kernel_param *kp)220{221 acpi_status status;222 const char *method = trace_method_name;223 u32 flags = 0;224 225/* So "xxx-once" comparison should go prior than "xxx" comparison */226#define acpi_compare_param(val, key) \227 strncmp((val), (key), sizeof(key) - 1)228 229 if (!acpi_compare_param(val, "enable")) {230 method = NULL;231 flags = ACPI_TRACE_ENABLED;232 } else if (!acpi_compare_param(val, "disable"))233 method = NULL;234 else if (!acpi_compare_param(val, "method-once"))235 flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT;236 else if (!acpi_compare_param(val, "method"))237 flags = ACPI_TRACE_ENABLED;238 else if (!acpi_compare_param(val, "opcode-once"))239 flags = ACPI_TRACE_ENABLED | ACPI_TRACE_ONESHOT | ACPI_TRACE_OPCODE;240 else if (!acpi_compare_param(val, "opcode"))241 flags = ACPI_TRACE_ENABLED | ACPI_TRACE_OPCODE;242 else243 return -EINVAL;244 245 status = acpi_debug_trace(method,246 acpi_gbl_trace_dbg_level,247 acpi_gbl_trace_dbg_layer,248 flags);249 if (ACPI_FAILURE(status))250 return -EBUSY;251 252 return 0;253}254 255static int param_get_trace_state(char *buffer, const struct kernel_param *kp)256{257 if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED))258 return sprintf(buffer, "disable\n");259 if (!acpi_gbl_trace_method_name)260 return sprintf(buffer, "enable\n");261 if (acpi_gbl_trace_flags & ACPI_TRACE_ONESHOT)262 return sprintf(buffer, "method-once\n");263 else264 return sprintf(buffer, "method\n");265}266 267module_param_call(trace_state, param_set_trace_state, param_get_trace_state,268 NULL, 0644);269#endif /* CONFIG_ACPI_DEBUG */270 271 272/* /sys/modules/acpi/parameters/aml_debug_output */273 274module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object,275 byte, 0644);276MODULE_PARM_DESC(aml_debug_output,277 "To enable/disable the ACPI Debug Object output.");278 279/* /sys/module/acpi/parameters/acpica_version */280static int param_get_acpica_version(char *buffer,281 const struct kernel_param *kp)282{283 int result;284 285 result = sprintf(buffer, "%x\n", ACPI_CA_VERSION);286 287 return result;288}289 290module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444);291 292/*293 * ACPI table sysfs I/F:294 * /sys/firmware/acpi/tables/295 * /sys/firmware/acpi/tables/data/296 * /sys/firmware/acpi/tables/dynamic/297 */298 299static LIST_HEAD(acpi_table_attr_list);300static struct kobject *tables_kobj;301static struct kobject *tables_data_kobj;302static struct kobject *dynamic_tables_kobj;303static struct kobject *hotplug_kobj;304 305#define ACPI_MAX_TABLE_INSTANCES 999306#define ACPI_INST_SIZE 4 /* including trailing 0 */307 308struct acpi_table_attr {309 struct bin_attribute attr;310 char name[ACPI_NAMESEG_SIZE];311 int instance;312 char filename[ACPI_NAMESEG_SIZE+ACPI_INST_SIZE];313 struct list_head node;314};315 316struct acpi_data_attr {317 struct bin_attribute attr;318 u64 addr;319};320 321static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,322 struct bin_attribute *bin_attr, char *buf,323 loff_t offset, size_t count)324{325 struct acpi_table_attr *table_attr =326 container_of(bin_attr, struct acpi_table_attr, attr);327 struct acpi_table_header *table_header = NULL;328 acpi_status status;329 ssize_t rc;330 331 status = acpi_get_table(table_attr->name, table_attr->instance,332 &table_header);333 if (ACPI_FAILURE(status))334 return -ENODEV;335 336 rc = memory_read_from_buffer(buf, count, &offset, table_header,337 table_header->length);338 acpi_put_table(table_header);339 return rc;340}341 342static int acpi_table_attr_init(struct kobject *tables_obj,343 struct acpi_table_attr *table_attr,344 struct acpi_table_header *table_header)345{346 struct acpi_table_header *header = NULL;347 struct acpi_table_attr *attr = NULL;348 char instance_str[ACPI_INST_SIZE];349 350 sysfs_attr_init(&table_attr->attr.attr);351 ACPI_COPY_NAMESEG(table_attr->name, table_header->signature);352 353 list_for_each_entry(attr, &acpi_table_attr_list, node) {354 if (ACPI_COMPARE_NAMESEG(table_attr->name, attr->name))355 if (table_attr->instance < attr->instance)356 table_attr->instance = attr->instance;357 }358 table_attr->instance++;359 if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) {360 pr_warn("%4.4s: too many table instances\n", table_attr->name);361 return -ERANGE;362 }363 364 ACPI_COPY_NAMESEG(table_attr->filename, table_header->signature);365 table_attr->filename[ACPI_NAMESEG_SIZE] = '\0';366 if (table_attr->instance > 1 || (table_attr->instance == 1 &&367 !acpi_get_table368 (table_header->signature, 2, &header))) {369 snprintf(instance_str, sizeof(instance_str), "%u",370 table_attr->instance);371 strcat(table_attr->filename, instance_str);372 }373 374 table_attr->attr.size = table_header->length;375 table_attr->attr.read = acpi_table_show;376 table_attr->attr.attr.name = table_attr->filename;377 table_attr->attr.attr.mode = 0400;378 379 return sysfs_create_bin_file(tables_obj, &table_attr->attr);380}381 382acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)383{384 struct acpi_table_attr *table_attr;385 386 switch (event) {387 case ACPI_TABLE_EVENT_INSTALL:388 table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);389 if (!table_attr)390 return AE_NO_MEMORY;391 392 if (acpi_table_attr_init(dynamic_tables_kobj,393 table_attr, table)) {394 kfree(table_attr);395 return AE_ERROR;396 }397 list_add_tail(&table_attr->node, &acpi_table_attr_list);398 break;399 case ACPI_TABLE_EVENT_LOAD:400 case ACPI_TABLE_EVENT_UNLOAD:401 case ACPI_TABLE_EVENT_UNINSTALL:402 /*403 * we do not need to do anything right now404 * because the table is not deleted from the405 * global table list when unloading it.406 */407 break;408 default:409 return AE_BAD_PARAMETER;410 }411 return AE_OK;412}413 414static ssize_t acpi_data_show(struct file *filp, struct kobject *kobj,415 struct bin_attribute *bin_attr, char *buf,416 loff_t offset, size_t count)417{418 struct acpi_data_attr *data_attr;419 void __iomem *base;420 ssize_t size;421 422 data_attr = container_of(bin_attr, struct acpi_data_attr, attr);423 size = data_attr->attr.size;424 425 if (offset < 0)426 return -EINVAL;427 428 if (offset >= size)429 return 0;430 431 if (count > size - offset)432 count = size - offset;433 434 base = acpi_os_map_iomem(data_attr->addr, size);435 if (!base)436 return -ENOMEM;437 438 memcpy_fromio(buf, base + offset, count);439 440 acpi_os_unmap_iomem(base, size);441 442 return count;443}444 445static int acpi_bert_data_init(void *th, struct acpi_data_attr *data_attr)446{447 struct acpi_table_bert *bert = th;448 449 if (bert->header.length < sizeof(struct acpi_table_bert) ||450 bert->region_length < sizeof(struct acpi_hest_generic_status)) {451 kfree(data_attr);452 return -EINVAL;453 }454 data_attr->addr = bert->address;455 data_attr->attr.size = bert->region_length;456 data_attr->attr.attr.name = "BERT";457 458 return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);459}460 461static int acpi_ccel_data_init(void *th, struct acpi_data_attr *data_attr)462{463 struct acpi_table_ccel *ccel = th;464 465 if (ccel->header.length < sizeof(struct acpi_table_ccel) ||466 !ccel->log_area_start_address || !ccel->log_area_minimum_length) {467 kfree(data_attr);468 return -EINVAL;469 }470 data_attr->addr = ccel->log_area_start_address;471 data_attr->attr.size = ccel->log_area_minimum_length;472 data_attr->attr.attr.name = "CCEL";473 474 return sysfs_create_bin_file(tables_data_kobj, &data_attr->attr);475}476 477static struct acpi_data_obj {478 char *name;479 int (*fn)(void *, struct acpi_data_attr *);480} acpi_data_objs[] = {481 { ACPI_SIG_BERT, acpi_bert_data_init },482 { ACPI_SIG_CCEL, acpi_ccel_data_init },483};484 485#define NUM_ACPI_DATA_OBJS ARRAY_SIZE(acpi_data_objs)486 487static int acpi_table_data_init(struct acpi_table_header *th)488{489 struct acpi_data_attr *data_attr;490 int i;491 492 for (i = 0; i < NUM_ACPI_DATA_OBJS; i++) {493 if (ACPI_COMPARE_NAMESEG(th->signature, acpi_data_objs[i].name)) {494 data_attr = kzalloc(sizeof(*data_attr), GFP_KERNEL);495 if (!data_attr)496 return -ENOMEM;497 sysfs_attr_init(&data_attr->attr.attr);498 data_attr->attr.read = acpi_data_show;499 data_attr->attr.attr.mode = 0400;500 return acpi_data_objs[i].fn(th, data_attr);501 }502 }503 return 0;504}505 506static int acpi_tables_sysfs_init(void)507{508 struct acpi_table_attr *table_attr;509 struct acpi_table_header *table_header = NULL;510 int table_index;511 acpi_status status;512 int ret;513 514 tables_kobj = kobject_create_and_add("tables", acpi_kobj);515 if (!tables_kobj)516 goto err;517 518 tables_data_kobj = kobject_create_and_add("data", tables_kobj);519 if (!tables_data_kobj)520 goto err_tables_data;521 522 dynamic_tables_kobj = kobject_create_and_add("dynamic", tables_kobj);523 if (!dynamic_tables_kobj)524 goto err_dynamic_tables;525 526 for (table_index = 0;; table_index++) {527 status = acpi_get_table_by_index(table_index, &table_header);528 529 if (status == AE_BAD_PARAMETER)530 break;531 532 if (ACPI_FAILURE(status))533 continue;534 535 table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);536 if (!table_attr)537 return -ENOMEM;538 539 ret = acpi_table_attr_init(tables_kobj,540 table_attr, table_header);541 if (ret) {542 kfree(table_attr);543 return ret;544 }545 list_add_tail(&table_attr->node, &acpi_table_attr_list);546 acpi_table_data_init(table_header);547 }548 549 kobject_uevent(tables_kobj, KOBJ_ADD);550 kobject_uevent(tables_data_kobj, KOBJ_ADD);551 kobject_uevent(dynamic_tables_kobj, KOBJ_ADD);552 553 return 0;554err_dynamic_tables:555 kobject_put(tables_data_kobj);556err_tables_data:557 kobject_put(tables_kobj);558err:559 return -ENOMEM;560}561 562/*563 * Detailed ACPI IRQ counters:564 * /sys/firmware/acpi/interrupts/565 */566 567u32 acpi_irq_handled;568u32 acpi_irq_not_handled;569 570#define COUNT_GPE 0571#define COUNT_SCI 1 /* acpi_irq_handled */572#define COUNT_SCI_NOT 2 /* acpi_irq_not_handled */573#define COUNT_ERROR 3 /* other */574#define NUM_COUNTERS_EXTRA 4575 576struct event_counter {577 u32 count;578 u32 flags;579};580 581static struct event_counter *all_counters;582static u32 num_gpes;583static u32 num_counters;584static struct attribute **all_attrs;585static u32 acpi_gpe_count;586 587static struct attribute_group interrupt_stats_attr_group = {588 .name = "interrupts",589};590 591static struct kobj_attribute *counter_attrs;592 593static void delete_gpe_attr_array(void)594{595 struct event_counter *tmp = all_counters;596 597 all_counters = NULL;598 kfree(tmp);599 600 if (counter_attrs) {601 int i;602 603 for (i = 0; i < num_gpes; i++)604 kfree(counter_attrs[i].attr.name);605 606 kfree(counter_attrs);607 }608 kfree(all_attrs);609}610 611static void gpe_count(u32 gpe_number)612{613 acpi_gpe_count++;614 615 if (!all_counters)616 return;617 618 if (gpe_number < num_gpes)619 all_counters[gpe_number].count++;620 else621 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS +622 COUNT_ERROR].count++;623}624 625static void fixed_event_count(u32 event_number)626{627 if (!all_counters)628 return;629 630 if (event_number < ACPI_NUM_FIXED_EVENTS)631 all_counters[num_gpes + event_number].count++;632 else633 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS +634 COUNT_ERROR].count++;635}636 637static void acpi_global_event_handler(u32 event_type, acpi_handle device,638 u32 event_number, void *context)639{640 if (event_type == ACPI_EVENT_TYPE_GPE) {641 gpe_count(event_number);642 pr_debug("GPE event 0x%02x\n", event_number);643 } else if (event_type == ACPI_EVENT_TYPE_FIXED) {644 fixed_event_count(event_number);645 pr_debug("Fixed event 0x%02x\n", event_number);646 } else {647 pr_debug("Other event 0x%02x\n", event_number);648 }649}650 651static int get_status(u32 index, acpi_event_status *ret,652 acpi_handle *handle)653{654 acpi_status status;655 656 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)657 return -EINVAL;658 659 if (index < num_gpes) {660 status = acpi_get_gpe_device(index, handle);661 if (ACPI_FAILURE(status)) {662 pr_warn("Invalid GPE 0x%x", index);663 return -ENXIO;664 }665 status = acpi_get_gpe_status(*handle, index, ret);666 } else {667 status = acpi_get_event_status(index - num_gpes, ret);668 }669 if (ACPI_FAILURE(status))670 return -EIO;671 672 return 0;673}674 675static ssize_t counter_show(struct kobject *kobj,676 struct kobj_attribute *attr, char *buf)677{678 int index = attr - counter_attrs;679 int size;680 acpi_handle handle;681 acpi_event_status status;682 int result = 0;683 684 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI].count =685 acpi_irq_handled;686 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT].count =687 acpi_irq_not_handled;688 all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count =689 acpi_gpe_count;690 size = sprintf(buf, "%8u", all_counters[index].count);691 692 /* "gpe_all" or "sci" */693 if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)694 goto end;695 696 result = get_status(index, &status, &handle);697 if (result)698 goto end;699 700 if (status & ACPI_EVENT_FLAG_ENABLE_SET)701 size += sprintf(buf + size, " EN");702 else703 size += sprintf(buf + size, " ");704 if (status & ACPI_EVENT_FLAG_STATUS_SET)705 size += sprintf(buf + size, " STS");706 else707 size += sprintf(buf + size, " ");708 709 if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER))710 size += sprintf(buf + size, " invalid ");711 else if (status & ACPI_EVENT_FLAG_ENABLED)712 size += sprintf(buf + size, " enabled ");713 else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)714 size += sprintf(buf + size, " wake_enabled");715 else716 size += sprintf(buf + size, " disabled ");717 if (status & ACPI_EVENT_FLAG_MASKED)718 size += sprintf(buf + size, " masked ");719 else720 size += sprintf(buf + size, " unmasked");721 722end:723 size += sprintf(buf + size, "\n");724 return result ? result : size;725}726 727/*728 * counter_set() sets the specified counter.729 * setting the total "sci" file to any value clears all counters.730 * enable/disable/clear a gpe/fixed event in user space.731 */732static ssize_t counter_set(struct kobject *kobj,733 struct kobj_attribute *attr, const char *buf,734 size_t size)735{736 int index = attr - counter_attrs;737 acpi_event_status status;738 acpi_handle handle;739 int result = 0;740 unsigned long tmp;741 742 if (index == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI) {743 int i;744 for (i = 0; i < num_counters; ++i)745 all_counters[i].count = 0;746 acpi_gpe_count = 0;747 acpi_irq_handled = 0;748 acpi_irq_not_handled = 0;749 goto end;750 }751 752 /* show the event status for both GPEs and Fixed Events */753 result = get_status(index, &status, &handle);754 if (result)755 goto end;756 757 if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) {758 pr_warn("Can not change Invalid GPE/Fixed Event status\n");759 return -EINVAL;760 }761 762 if (index < num_gpes) {763 if (!strcmp(buf, "disable\n") &&764 (status & ACPI_EVENT_FLAG_ENABLED))765 result = acpi_disable_gpe(handle, index);766 else if (!strcmp(buf, "enable\n") &&767 !(status & ACPI_EVENT_FLAG_ENABLED))768 result = acpi_enable_gpe(handle, index);769 else if (!strcmp(buf, "clear\n") &&770 (status & ACPI_EVENT_FLAG_STATUS_SET))771 result = acpi_clear_gpe(handle, index);772 else if (!strcmp(buf, "mask\n"))773 result = acpi_mask_gpe(handle, index, TRUE);774 else if (!strcmp(buf, "unmask\n"))775 result = acpi_mask_gpe(handle, index, FALSE);776 else if (!kstrtoul(buf, 0, &tmp))777 all_counters[index].count = tmp;778 else779 result = -EINVAL;780 } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) {781 int event = index - num_gpes;782 if (!strcmp(buf, "disable\n") &&783 (status & ACPI_EVENT_FLAG_ENABLE_SET))784 result = acpi_disable_event(event, ACPI_NOT_ISR);785 else if (!strcmp(buf, "enable\n") &&786 !(status & ACPI_EVENT_FLAG_ENABLE_SET))787 result = acpi_enable_event(event, ACPI_NOT_ISR);788 else if (!strcmp(buf, "clear\n") &&789 (status & ACPI_EVENT_FLAG_STATUS_SET))790 result = acpi_clear_event(event);791 else if (!kstrtoul(buf, 0, &tmp))792 all_counters[index].count = tmp;793 else794 result = -EINVAL;795 } else796 all_counters[index].count = strtoul(buf, NULL, 0);797 798 if (ACPI_FAILURE(result))799 result = -EINVAL;800end:801 return result ? result : size;802}803 804/*805 * A Quirk Mechanism for GPE Flooding Prevention:806 *807 * Quirks may be needed to prevent GPE flooding on a specific GPE. The808 * flooding typically cannot be detected and automatically prevented by809 * ACPI_GPE_DISPATCH_NONE check because there is a _Lxx/_Exx prepared in810 * the AML tables. This normally indicates a feature gap in Linux, thus811 * instead of providing endless quirk tables, we provide a boot parameter812 * for those who want this quirk. For example, if the users want to prevent813 * the GPE flooding for GPE 00, they need to specify the following boot814 * parameter:815 * acpi_mask_gpe=0x00816 * Note, the parameter can be a list (see bitmap_parselist() for the details).817 * The masking status can be modified by the following runtime controlling818 * interface:819 * echo unmask > /sys/firmware/acpi/interrupts/gpe00820 */821#define ACPI_MASKABLE_GPE_MAX 0x100822static DECLARE_BITMAP(acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) __initdata;823 824static int __init acpi_gpe_set_masked_gpes(char *val)825{826 int ret;827 u8 gpe;828 829 ret = kstrtou8(val, 0, &gpe);830 if (ret) {831 ret = bitmap_parselist(val, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX);832 if (ret)833 return ret;834 } else835 set_bit(gpe, acpi_masked_gpes_map);836 837 return 1;838}839__setup("acpi_mask_gpe=", acpi_gpe_set_masked_gpes);840 841void __init acpi_gpe_apply_masked_gpes(void)842{843 acpi_handle handle;844 acpi_status status;845 u16 gpe;846 847 for_each_set_bit(gpe, acpi_masked_gpes_map, ACPI_MASKABLE_GPE_MAX) {848 status = acpi_get_gpe_device(gpe, &handle);849 if (ACPI_SUCCESS(status)) {850 pr_info("Masking GPE 0x%x.\n", gpe);851 (void)acpi_mask_gpe(handle, gpe, TRUE);852 }853 }854}855 856void acpi_irq_stats_init(void)857{858 acpi_status status;859 int i;860 861 if (all_counters)862 return;863 864 num_gpes = acpi_current_gpe_count;865 num_counters = num_gpes + ACPI_NUM_FIXED_EVENTS + NUM_COUNTERS_EXTRA;866 867 all_attrs = kcalloc(num_counters + 1, sizeof(*all_attrs), GFP_KERNEL);868 if (all_attrs == NULL)869 return;870 871 all_counters = kcalloc(num_counters, sizeof(*all_counters), GFP_KERNEL);872 if (all_counters == NULL)873 goto fail;874 875 status = acpi_install_global_event_handler(acpi_global_event_handler, NULL);876 if (ACPI_FAILURE(status))877 goto fail;878 879 counter_attrs = kcalloc(num_counters, sizeof(*counter_attrs), GFP_KERNEL);880 if (counter_attrs == NULL)881 goto fail;882 883 for (i = 0; i < num_counters; ++i) {884 char buffer[12];885 char *name;886 887 if (i < num_gpes)888 sprintf(buffer, "gpe%02X", i);889 else if (i == num_gpes + ACPI_EVENT_PMTIMER)890 sprintf(buffer, "ff_pmtimer");891 else if (i == num_gpes + ACPI_EVENT_GLOBAL)892 sprintf(buffer, "ff_gbl_lock");893 else if (i == num_gpes + ACPI_EVENT_POWER_BUTTON)894 sprintf(buffer, "ff_pwr_btn");895 else if (i == num_gpes + ACPI_EVENT_SLEEP_BUTTON)896 sprintf(buffer, "ff_slp_btn");897 else if (i == num_gpes + ACPI_EVENT_RTC)898 sprintf(buffer, "ff_rt_clk");899 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE)900 sprintf(buffer, "gpe_all");901 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI)902 sprintf(buffer, "sci");903 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_SCI_NOT)904 sprintf(buffer, "sci_not");905 else if (i == num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_ERROR)906 sprintf(buffer, "error");907 else908 sprintf(buffer, "bug%02X", i);909 910 name = kstrdup(buffer, GFP_KERNEL);911 if (name == NULL)912 goto fail;913 914 sysfs_attr_init(&counter_attrs[i].attr);915 counter_attrs[i].attr.name = name;916 counter_attrs[i].attr.mode = 0644;917 counter_attrs[i].show = counter_show;918 counter_attrs[i].store = counter_set;919 920 all_attrs[i] = &counter_attrs[i].attr;921 }922 923 interrupt_stats_attr_group.attrs = all_attrs;924 if (!sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group))925 return;926 927fail:928 delete_gpe_attr_array();929}930 931static void __exit interrupt_stats_exit(void)932{933 sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);934 935 delete_gpe_attr_array();936}937 938static ssize_t pm_profile_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)939{940 return sprintf(buf, "%d\n", acpi_gbl_FADT.preferred_profile);941}942 943static const struct kobj_attribute pm_profile_attr = __ATTR_RO(pm_profile);944 945static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)946{947 struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);948 949 return sprintf(buf, "%d\n", hotplug->enabled);950}951 952static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,953 const char *buf, size_t size)954{955 struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj);956 unsigned int val;957 958 if (kstrtouint(buf, 10, &val) || val > 1)959 return -EINVAL;960 961 acpi_scan_hotplug_enabled(hotplug, val);962 return size;963}964 965static struct kobj_attribute hotplug_enabled_attr = __ATTR_RW(enabled);966 967static struct attribute *hotplug_profile_attrs[] = {968 &hotplug_enabled_attr.attr,969 NULL970};971ATTRIBUTE_GROUPS(hotplug_profile);972 973static const struct kobj_type acpi_hotplug_profile_ktype = {974 .sysfs_ops = &kobj_sysfs_ops,975 .default_groups = hotplug_profile_groups,976};977 978void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,979 const char *name)980{981 int error;982 983 if (!hotplug_kobj)984 goto err_out;985 986 error = kobject_init_and_add(&hotplug->kobj,987 &acpi_hotplug_profile_ktype, hotplug_kobj, "%s", name);988 if (error) {989 kobject_put(&hotplug->kobj);990 goto err_out;991 }992 993 kobject_uevent(&hotplug->kobj, KOBJ_ADD);994 return;995 996 err_out:997 pr_err("Unable to add hotplug profile '%s'\n", name);998}999 1000static ssize_t force_remove_show(struct kobject *kobj,1001 struct kobj_attribute *attr, char *buf)1002{1003 return sprintf(buf, "%d\n", 0);1004}1005 1006static ssize_t force_remove_store(struct kobject *kobj,1007 struct kobj_attribute *attr,1008 const char *buf, size_t size)1009{1010 bool val;1011 int ret;1012 1013 ret = kstrtobool(buf, &val);1014 if (ret < 0)1015 return ret;1016 1017 if (val) {1018 pr_err("Enabling force_remove is not supported anymore. Please report to linux-acpi@vger.kernel.org if you depend on this functionality\n");1019 return -EINVAL;1020 }1021 return size;1022}1023 1024static const struct kobj_attribute force_remove_attr = __ATTR_RW(force_remove);1025 1026int __init acpi_sysfs_init(void)1027{1028 int result;1029 1030 result = acpi_tables_sysfs_init();1031 if (result)1032 return result;1033 1034 hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj);1035 if (!hotplug_kobj)1036 return -ENOMEM;1037 1038 result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr);1039 if (result)1040 return result;1041 1042 result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);1043 return result;1044}1045