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