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