Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI: add support for loading SSDTs via configfs

New tables can be loaded by creating directories under /config/table/
and writing the AML code into the aml table attribute. Various table
attributes will be readable once the table is successfully loaded.

Unloading tables is not supported at the moment, but it can be easily
implemented once ACPI loading functions provide a table handle to be
used for unloading.

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Octavian Purdila and committed by
Rafael J. Wysocki
612bd01f 0bf54fcd

+258 -1
+29
Documentation/ABI/testing/configfs-acpi
··· 5 5 Description: 6 6 This represents the ACPI subsystem entry point directory. It 7 7 contains sub-groups corresponding to ACPI configurable options. 8 + 9 + What: /config/acpi/table 10 + Date: July 2016 11 + KernelVersion: 4.8 12 + Description: 13 + 14 + This group contains the configuration for user defined ACPI 15 + tables. The attributes of a user define table are: 16 + 17 + aml - a binary attribute that the user can use to 18 + fill in the ACPI aml definitions. Once the aml 19 + data is written to this file and the file is 20 + closed the table will be loaded and ACPI devices 21 + will be enumerated. To check if the operation is 22 + successful the user must check the error code 23 + for close(). If the operation is successful, 24 + subsequent writes to this attribute will fail. 25 + 26 + The rest of the attributes are read-only and are valid only 27 + after the table has been loaded by filling the aml entry: 28 + 29 + signature - ASCII table signature 30 + length - length of table in bytes, including the header 31 + revision - ACPI Specification minor version number 32 + oem_id - ASCII OEM identification 33 + oem_table_id - ASCII OEM table identification 34 + oem_revision - OEM revision number 35 + asl_compiler_id - ASCII ASL compiler vendor ID 36 + asl_compiler_revision - ASL compiler version
+14
Documentation/acpi/ssdt-overlays.txt
··· 156 156 /bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp 157 157 dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp) 158 158 rm $tmp 159 + 160 + == Loading ACPI SSDTs from configfs == 161 + 162 + This option allows loading of user defined SSDTs from userspace via the configfs 163 + interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be 164 + mounted. In the following examples, we assume that configfs has been mounted in 165 + /config. 166 + 167 + New tables can be loading by creating new directories in /config/acpi/table/ and 168 + writing the SSDT aml code in the aml attribute: 169 + 170 + cd /config/acpi/table 171 + mkdir my_ssdt 172 + cat ~/ssdt.aml > my_ssdt/aml
+215 -1
drivers/acpi/configfs.c
··· 8 8 * the Free Software Foundation. 9 9 */ 10 10 11 + #define pr_fmt(fmt) "ACPI configfs: " fmt 12 + 11 13 #include <linux/init.h> 12 14 #include <linux/module.h> 13 15 #include <linux/configfs.h> 14 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 + }; 15 226 16 227 static struct config_item_type acpi_root_group_type = { 17 228 .ct_owner = THIS_MODULE, ··· 249 38 if (ret) 250 39 return ret; 251 40 252 - return 0; 41 + acpi_table_group = configfs_register_default_group(root, "table", 42 + &acpi_tables_type); 43 + return PTR_ERR_OR_ZERO(acpi_table_group); 253 44 } 254 45 module_init(acpi_configfs_init); 255 46 256 47 static void __exit acpi_configfs_exit(void) 257 48 { 49 + configfs_unregister_default_group(acpi_table_group); 258 50 configfs_unregister_subsystem(&acpi_configfs); 259 51 } 260 52 module_exit(acpi_configfs_exit);