18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI configfs support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Intel Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI configfs: " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/configfs.h> 138c2ecf20Sopenharmony_ci#include <linux/acpi.h> 148c2ecf20Sopenharmony_ci#include <linux/security.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "acpica/accommon.h" 178c2ecf20Sopenharmony_ci#include "acpica/actables.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic struct config_group *acpi_table_group; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct acpi_table { 228c2ecf20Sopenharmony_ci struct config_item cfg; 238c2ecf20Sopenharmony_ci struct acpi_table_header *header; 248c2ecf20Sopenharmony_ci u32 index; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic ssize_t acpi_table_aml_write(struct config_item *cfg, 288c2ecf20Sopenharmony_ci const void *data, size_t size) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci const struct acpi_table_header *header = data; 318c2ecf20Sopenharmony_ci struct acpi_table *table; 328c2ecf20Sopenharmony_ci int ret = security_locked_down(LOCKDOWN_ACPI_TABLES); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (ret) 358c2ecf20Sopenharmony_ci return ret; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci table = container_of(cfg, struct acpi_table, cfg); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (table->header) { 408c2ecf20Sopenharmony_ci pr_err("table already loaded\n"); 418c2ecf20Sopenharmony_ci return -EBUSY; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (header->length != size) { 458c2ecf20Sopenharmony_ci pr_err("invalid table length\n"); 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) { 508c2ecf20Sopenharmony_ci pr_err("invalid table signature\n"); 518c2ecf20Sopenharmony_ci return -EINVAL; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci table = container_of(cfg, struct acpi_table, cfg); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci table->header = kmemdup(header, header->length, GFP_KERNEL); 578c2ecf20Sopenharmony_ci if (!table->header) 588c2ecf20Sopenharmony_ci return -ENOMEM; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ret = acpi_load_table(table->header, &table->index); 618c2ecf20Sopenharmony_ci if (ret) { 628c2ecf20Sopenharmony_ci kfree(table->header); 638c2ecf20Sopenharmony_ci table->header = NULL; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return ret; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline struct acpi_table_header *get_header(struct config_item *cfg) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!table->header) 748c2ecf20Sopenharmony_ci pr_err("table not loaded\n"); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return table->header; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic ssize_t acpi_table_aml_read(struct config_item *cfg, 808c2ecf20Sopenharmony_ci void *data, size_t size) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (!h) 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (data) 888c2ecf20Sopenharmony_ci memcpy(data, h, h->length); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return h->length; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define MAX_ACPI_TABLE_SIZE (128 * 1024) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciCONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct configfs_bin_attribute *acpi_table_bin_attrs[] = { 988c2ecf20Sopenharmony_ci &acpi_table_attr_aml, 998c2ecf20Sopenharmony_ci NULL, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!h) 1078c2ecf20Sopenharmony_ci return -EINVAL; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return sprintf(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->signature); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic ssize_t acpi_table_length_show(struct config_item *cfg, char *str) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (!h) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return sprintf(str, "%d\n", h->length); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!h) 1278c2ecf20Sopenharmony_ci return -EINVAL; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return sprintf(str, "%d\n", h->revision); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!h) 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!h) 1478c2ecf20Sopenharmony_ci return -EINVAL; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!h) 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return sprintf(str, "%d\n", h->oem_revision); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, 1638c2ecf20Sopenharmony_ci char *str) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!h) 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return sprintf(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->asl_compiler_id); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, 1748c2ecf20Sopenharmony_ci char *str) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct acpi_table_header *h = get_header(cfg); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!h) 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return sprintf(str, "%d\n", h->asl_compiler_revision); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, signature); 1858c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, length); 1868c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, revision); 1878c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, oem_id); 1888c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, oem_table_id); 1898c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, oem_revision); 1908c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); 1918c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic struct configfs_attribute *acpi_table_attrs[] = { 1948c2ecf20Sopenharmony_ci &acpi_table_attr_signature, 1958c2ecf20Sopenharmony_ci &acpi_table_attr_length, 1968c2ecf20Sopenharmony_ci &acpi_table_attr_revision, 1978c2ecf20Sopenharmony_ci &acpi_table_attr_oem_id, 1988c2ecf20Sopenharmony_ci &acpi_table_attr_oem_table_id, 1998c2ecf20Sopenharmony_ci &acpi_table_attr_oem_revision, 2008c2ecf20Sopenharmony_ci &acpi_table_attr_asl_compiler_id, 2018c2ecf20Sopenharmony_ci &acpi_table_attr_asl_compiler_revision, 2028c2ecf20Sopenharmony_ci NULL, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic const struct config_item_type acpi_table_type = { 2068c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 2078c2ecf20Sopenharmony_ci .ct_bin_attrs = acpi_table_bin_attrs, 2088c2ecf20Sopenharmony_ci .ct_attrs = acpi_table_attrs, 2098c2ecf20Sopenharmony_ci}; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct config_item *acpi_table_make_item(struct config_group *group, 2128c2ecf20Sopenharmony_ci const char *name) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct acpi_table *table; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci table = kzalloc(sizeof(*table), GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!table) 2188c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci config_item_init_type_name(&table->cfg, name, &acpi_table_type); 2218c2ecf20Sopenharmony_ci return &table->cfg; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void acpi_table_drop_item(struct config_group *group, 2258c2ecf20Sopenharmony_ci struct config_item *cfg) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ACPI_INFO(("Host-directed Dynamic ACPI Table Unload")); 2308c2ecf20Sopenharmony_ci acpi_unload_table(table->index); 2318c2ecf20Sopenharmony_ci config_item_put(cfg); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic struct configfs_group_operations acpi_table_group_ops = { 2358c2ecf20Sopenharmony_ci .make_item = acpi_table_make_item, 2368c2ecf20Sopenharmony_ci .drop_item = acpi_table_drop_item, 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct config_item_type acpi_tables_type = { 2408c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 2418c2ecf20Sopenharmony_ci .ct_group_ops = &acpi_table_group_ops, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic const struct config_item_type acpi_root_group_type = { 2458c2ecf20Sopenharmony_ci .ct_owner = THIS_MODULE, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct configfs_subsystem acpi_configfs = { 2498c2ecf20Sopenharmony_ci .su_group = { 2508c2ecf20Sopenharmony_ci .cg_item = { 2518c2ecf20Sopenharmony_ci .ci_namebuf = "acpi", 2528c2ecf20Sopenharmony_ci .ci_type = &acpi_root_group_type, 2538c2ecf20Sopenharmony_ci }, 2548c2ecf20Sopenharmony_ci }, 2558c2ecf20Sopenharmony_ci .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex), 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int __init acpi_configfs_init(void) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int ret; 2618c2ecf20Sopenharmony_ci struct config_group *root = &acpi_configfs.su_group; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci config_group_init(root); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = configfs_register_subsystem(&acpi_configfs); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci acpi_table_group = configfs_register_default_group(root, "table", 2708c2ecf20Sopenharmony_ci &acpi_tables_type); 2718c2ecf20Sopenharmony_ci if (IS_ERR(acpi_table_group)) { 2728c2ecf20Sopenharmony_ci configfs_unregister_subsystem(&acpi_configfs); 2738c2ecf20Sopenharmony_ci return PTR_ERR(acpi_table_group); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_cimodule_init(acpi_configfs_init); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void __exit acpi_configfs_exit(void) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci configfs_unregister_default_group(acpi_table_group); 2838c2ecf20Sopenharmony_ci configfs_unregister_subsystem(&acpi_configfs); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_cimodule_exit(acpi_configfs_exit); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); 2888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI configfs support"); 2898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 290