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