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