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