aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOctavian Purdila <octavian.purdila@intel.com>2016-07-08 12:13:14 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-07-08 15:52:36 -0400
commit612bd01fc6e04c3ce9eb59587b4a7e4ebd6aff35 (patch)
tree00b1df2c43e46b3a7f8dd47cb954b077e19d772e
parent0bf54fcd95042bd178cb25368422cf4474fc8492 (diff)
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>
-rw-r--r--Documentation/ABI/testing/configfs-acpi29
-rw-r--r--Documentation/acpi/ssdt-overlays.txt14
-rw-r--r--drivers/acpi/configfs.c216
3 files changed, 258 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/configfs-acpi b/Documentation/ABI/testing/configfs-acpi
index 17b19dc2822d..4ab4e99aa863 100644
--- a/Documentation/ABI/testing/configfs-acpi
+++ b/Documentation/ABI/testing/configfs-acpi
@@ -5,3 +5,32 @@ Contact: linux-acpi@vger.kernel.org
5Description: 5Description:
6 This represents the ACPI subsystem entry point directory. It 6 This represents the ACPI subsystem entry point directory. It
7 contains sub-groups corresponding to ACPI configurable options. 7 contains sub-groups corresponding to ACPI configurable options.
8
9What: /config/acpi/table
10Date: July 2016
11KernelVersion: 4.8
12Description:
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
diff --git a/Documentation/acpi/ssdt-overlays.txt b/Documentation/acpi/ssdt-overlays.txt
index c4b57badd0b7..5ae13f161ea2 100644
--- a/Documentation/acpi/ssdt-overlays.txt
+++ b/Documentation/acpi/ssdt-overlays.txt
@@ -156,3 +156,17 @@ tmp=$(mktemp)
156/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp 156/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
157dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp) 157dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
158rm $tmp 158rm $tmp
159
160== Loading ACPI SSDTs from configfs ==
161
162This option allows loading of user defined SSDTs from userspace via the configfs
163interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be
164mounted. In the following examples, we assume that configfs has been mounted in
165/config.
166
167New tables can be loading by creating new directories in /config/acpi/table/ and
168writing the SSDT aml code in the aml attribute:
169
170cd /config/acpi/table
171mkdir my_ssdt
172cat ~/ssdt.aml > my_ssdt/aml
diff --git a/drivers/acpi/configfs.c b/drivers/acpi/configfs.c
index 44b463f6fca5..146a77fb762d 100644
--- a/drivers/acpi/configfs.c
+++ b/drivers/acpi/configfs.c
@@ -8,11 +8,222 @@
8 * the Free Software Foundation. 8 * the Free Software Foundation.
9 */ 9 */
10 10
11#define pr_fmt(fmt) "ACPI configfs: " fmt
12
11#include <linux/init.h> 13#include <linux/init.h>
12#include <linux/module.h> 14#include <linux/module.h>
13#include <linux/configfs.h> 15#include <linux/configfs.h>
14#include <linux/acpi.h> 16#include <linux/acpi.h>
15 17
18static struct config_group *acpi_table_group;
19
20struct acpi_table {
21 struct config_item cfg;
22 struct acpi_table_header *header;
23};
24
25static 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
64static 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
74static 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
90CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE);
91
92struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
93 &acpi_table_attr_aml,
94 NULL,
95};
96
97ssize_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
107ssize_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
117ssize_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
127ssize_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
137ssize_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
147ssize_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
157ssize_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
167ssize_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
178CONFIGFS_ATTR_RO(acpi_table_, signature);
179CONFIGFS_ATTR_RO(acpi_table_, length);
180CONFIGFS_ATTR_RO(acpi_table_, revision);
181CONFIGFS_ATTR_RO(acpi_table_, oem_id);
182CONFIGFS_ATTR_RO(acpi_table_, oem_table_id);
183CONFIGFS_ATTR_RO(acpi_table_, oem_revision);
184CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id);
185CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision);
186
187struct 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
199static 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
205static 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
218struct configfs_group_operations acpi_table_group_ops = {
219 .make_item = acpi_table_make_item,
220};
221
222static struct config_item_type acpi_tables_type = {
223 .ct_owner = THIS_MODULE,
224 .ct_group_ops = &acpi_table_group_ops,
225};
226
16static struct config_item_type acpi_root_group_type = { 227static struct config_item_type acpi_root_group_type = {
17 .ct_owner = THIS_MODULE, 228 .ct_owner = THIS_MODULE,
18}; 229};
@@ -38,12 +249,15 @@ static int __init acpi_configfs_init(void)
38 if (ret) 249 if (ret)
39 return ret; 250 return ret;
40 251
41 return 0; 252 acpi_table_group = configfs_register_default_group(root, "table",
253 &acpi_tables_type);
254 return PTR_ERR_OR_ZERO(acpi_table_group);
42} 255}
43module_init(acpi_configfs_init); 256module_init(acpi_configfs_init);
44 257
45static void __exit acpi_configfs_exit(void) 258static void __exit acpi_configfs_exit(void)
46{ 259{
260 configfs_unregister_default_group(acpi_table_group);
47 configfs_unregister_subsystem(&acpi_configfs); 261 configfs_unregister_subsystem(&acpi_configfs);
48} 262}
49module_exit(acpi_configfs_exit); 263module_exit(acpi_configfs_exit);