1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ACPI configfs support 4 * 5 * Copyright (c) 2016 Intel Corporation 6 */ 7 8 #define pr_fmt(fmt) "ACPI configfs: " fmt 9 10 #include <linux/init.h> 11 #include <linux/module.h> 12 #include <linux/configfs.h> 13 #include <linux/acpi.h> 14 15 #include "acpica/accommon.h" 16 #include "acpica/actables.h" 17 18 static struct config_group *acpi_table_group; 19 20 struct acpi_table { 21 struct config_item cfg; 22 struct acpi_table_header *header; 23 u32 index; 24 }; 25 26 static ssize_t acpi_table_aml_write(struct config_item *cfg, 27 const void *data, size_t size) 28 { 29 const struct acpi_table_header *header = data; 30 struct acpi_table *table; 31 int ret; 32 33 table = container_of(cfg, struct acpi_table, cfg); 34 35 if (table->header) { 36 pr_err("table already loaded\n"); 37 return -EBUSY; 38 } 39 40 if (header->length != size) { 41 pr_err("invalid table length\n"); 42 return -EINVAL; 43 } 44 45 if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) { 46 pr_err("invalid table signature\n"); 47 return -EINVAL; 48 } 49 50 table = container_of(cfg, struct acpi_table, cfg); 51 52 table->header = kmemdup(header, header->length, GFP_KERNEL); 53 if (!table->header) 54 return -ENOMEM; 55 56 ret = acpi_load_table(table->header, &table->index); 57 if (ret) { 58 kfree(table->header); 59 table->header = NULL; 60 } 61 62 return ret; 63 } 64 65 static inline struct acpi_table_header *get_header(struct config_item *cfg) 66 { 67 struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); 68 69 if (!table->header) 70 pr_err("table not loaded\n"); 71 72 return table->header; 73 } 74 75 static ssize_t acpi_table_aml_read(struct config_item *cfg, 76 void *data, size_t size) 77 { 78 struct acpi_table_header *h = get_header(cfg); 79 80 if (!h) 81 return -EINVAL; 82 83 if (data) 84 memcpy(data, h, h->length); 85 86 return h->length; 87 } 88 89 #define MAX_ACPI_TABLE_SIZE (128 * 1024) 90 91 CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE); 92 93 static struct configfs_bin_attribute *acpi_table_bin_attrs[] = { 94 &acpi_table_attr_aml, 95 NULL, 96 }; 97 98 static ssize_t acpi_table_signature_show(struct config_item *cfg, char *str) 99 { 100 struct acpi_table_header *h = get_header(cfg); 101 102 if (!h) 103 return -EINVAL; 104 105 return sprintf(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->signature); 106 } 107 108 static ssize_t acpi_table_length_show(struct config_item *cfg, char *str) 109 { 110 struct acpi_table_header *h = get_header(cfg); 111 112 if (!h) 113 return -EINVAL; 114 115 return sprintf(str, "%d\n", h->length); 116 } 117 118 static ssize_t acpi_table_revision_show(struct config_item *cfg, char *str) 119 { 120 struct acpi_table_header *h = get_header(cfg); 121 122 if (!h) 123 return -EINVAL; 124 125 return sprintf(str, "%d\n", h->revision); 126 } 127 128 static ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str) 129 { 130 struct acpi_table_header *h = get_header(cfg); 131 132 if (!h) 133 return -EINVAL; 134 135 return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id); 136 } 137 138 static ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str) 139 { 140 struct acpi_table_header *h = get_header(cfg); 141 142 if (!h) 143 return -EINVAL; 144 145 return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id); 146 } 147 148 static ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str) 149 { 150 struct acpi_table_header *h = get_header(cfg); 151 152 if (!h) 153 return -EINVAL; 154 155 return sprintf(str, "%d\n", h->oem_revision); 156 } 157 158 static ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, 159 char *str) 160 { 161 struct acpi_table_header *h = get_header(cfg); 162 163 if (!h) 164 return -EINVAL; 165 166 return sprintf(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->asl_compiler_id); 167 } 168 169 static ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg, 170 char *str) 171 { 172 struct acpi_table_header *h = get_header(cfg); 173 174 if (!h) 175 return -EINVAL; 176 177 return sprintf(str, "%d\n", h->asl_compiler_revision); 178 } 179 180 CONFIGFS_ATTR_RO(acpi_table_, signature); 181 CONFIGFS_ATTR_RO(acpi_table_, length); 182 CONFIGFS_ATTR_RO(acpi_table_, revision); 183 CONFIGFS_ATTR_RO(acpi_table_, oem_id); 184 CONFIGFS_ATTR_RO(acpi_table_, oem_table_id); 185 CONFIGFS_ATTR_RO(acpi_table_, oem_revision); 186 CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id); 187 CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision); 188 189 static struct configfs_attribute *acpi_table_attrs[] = { 190 &acpi_table_attr_signature, 191 &acpi_table_attr_length, 192 &acpi_table_attr_revision, 193 &acpi_table_attr_oem_id, 194 &acpi_table_attr_oem_table_id, 195 &acpi_table_attr_oem_revision, 196 &acpi_table_attr_asl_compiler_id, 197 &acpi_table_attr_asl_compiler_revision, 198 NULL, 199 }; 200 201 static const struct config_item_type acpi_table_type = { 202 .ct_owner = THIS_MODULE, 203 .ct_bin_attrs = acpi_table_bin_attrs, 204 .ct_attrs = acpi_table_attrs, 205 }; 206 207 static struct config_item *acpi_table_make_item(struct config_group *group, 208 const char *name) 209 { 210 struct acpi_table *table; 211 212 table = kzalloc(sizeof(*table), GFP_KERNEL); 213 if (!table) 214 return ERR_PTR(-ENOMEM); 215 216 config_item_init_type_name(&table->cfg, name, &acpi_table_type); 217 return &table->cfg; 218 } 219 220 static void acpi_table_drop_item(struct config_group *group, 221 struct config_item *cfg) 222 { 223 struct acpi_table *table = container_of(cfg, struct acpi_table, cfg); 224 225 ACPI_INFO(("Host-directed Dynamic ACPI Table Unload")); 226 acpi_unload_table(table->index); 227 } 228 229 static struct configfs_group_operations acpi_table_group_ops = { 230 .make_item = acpi_table_make_item, 231 .drop_item = acpi_table_drop_item, 232 }; 233 234 static const struct config_item_type acpi_tables_type = { 235 .ct_owner = THIS_MODULE, 236 .ct_group_ops = &acpi_table_group_ops, 237 }; 238 239 static const struct config_item_type acpi_root_group_type = { 240 .ct_owner = THIS_MODULE, 241 }; 242 243 static struct configfs_subsystem acpi_configfs = { 244 .su_group = { 245 .cg_item = { 246 .ci_namebuf = "acpi", 247 .ci_type = &acpi_root_group_type, 248 }, 249 }, 250 .su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex), 251 }; 252 253 static int __init acpi_configfs_init(void) 254 { 255 int ret; 256 struct config_group *root = &acpi_configfs.su_group; 257 258 config_group_init(root); 259 260 ret = configfs_register_subsystem(&acpi_configfs); 261 if (ret) 262 return ret; 263 264 acpi_table_group = configfs_register_default_group(root, "table", 265 &acpi_tables_type); 266 return PTR_ERR_OR_ZERO(acpi_table_group); 267 } 268 module_init(acpi_configfs_init); 269 270 static void __exit acpi_configfs_exit(void) 271 { 272 configfs_unregister_default_group(acpi_table_group); 273 configfs_unregister_subsystem(&acpi_configfs); 274 } 275 module_exit(acpi_configfs_exit); 276 277 MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); 278 MODULE_DESCRIPTION("ACPI configfs support"); 279 MODULE_LICENSE("GPL v2"); 280