Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * ACPI configfs support
  4 *
  5 * Copyright (c) 2016 Intel Corporation
  6 */
  7
  8#define pr_fmt(fmt) "ACPI configfs: " fmt
  9
 10#include <linux/init.h>
 11#include <linux/module.h>
 12#include <linux/configfs.h>
 13#include <linux/acpi.h>
 14#include <linux/security.h>
 15
 16#include "acpica/accommon.h"
 17#include "acpica/actables.h"
 18
 19static struct config_group *acpi_table_group;
 20
 21struct acpi_table {
 22	struct config_item cfg;
 23	struct acpi_table_header *header;
 24	u32 index;
 25};
 26
 27static 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;
 32	int ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
 33
 34	if (ret)
 35		return ret;
 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
 60	ret = acpi_load_table(table->header, &table->index);
 61	if (ret) {
 62		kfree(table->header);
 63		table->header = NULL;
 64	}
 65
 66	return ret;
 67}
 68
 69static 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
 79static 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
 95CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE);
 96
 97static struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
 98	&acpi_table_attr_aml,
 99	NULL,
100};
101
102static ssize_t acpi_table_signature_show(struct config_item *cfg, char *str)
103{
104	struct acpi_table_header *h = get_header(cfg);
105
106	if (!h)
107		return -EINVAL;
108
109	return sprintf(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->signature);
110}
111
112static ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
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
122static ssize_t acpi_table_revision_show(struct config_item *cfg, char *str)
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
132static ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str)
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
142static ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str)
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
152static ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str)
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
162static ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg,
163					       char *str)
164{
165	struct acpi_table_header *h = get_header(cfg);
166
167	if (!h)
168		return -EINVAL;
169
170	return sprintf(str, "%.*s\n", ACPI_NAMESEG_SIZE, h->asl_compiler_id);
171}
172
173static ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg,
174						     char *str)
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
184CONFIGFS_ATTR_RO(acpi_table_, signature);
185CONFIGFS_ATTR_RO(acpi_table_, length);
186CONFIGFS_ATTR_RO(acpi_table_, revision);
187CONFIGFS_ATTR_RO(acpi_table_, oem_id);
188CONFIGFS_ATTR_RO(acpi_table_, oem_table_id);
189CONFIGFS_ATTR_RO(acpi_table_, oem_revision);
190CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id);
191CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision);
192
193static struct configfs_attribute *acpi_table_attrs[] = {
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
205static const struct config_item_type acpi_table_type = {
206	.ct_owner = THIS_MODULE,
207	.ct_bin_attrs = acpi_table_bin_attrs,
208	.ct_attrs = acpi_table_attrs,
209};
210
211static 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
224static 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"));
230	acpi_unload_table(table->index);
231}
232
233static struct configfs_group_operations acpi_table_group_ops = {
234	.make_item = acpi_table_make_item,
235	.drop_item = acpi_table_drop_item,
236};
237
238static const struct config_item_type acpi_tables_type = {
239	.ct_owner = THIS_MODULE,
240	.ct_group_ops = &acpi_table_group_ops,
241};
242
243static const struct config_item_type acpi_root_group_type = {
244	.ct_owner = THIS_MODULE,
245};
246
247static struct configfs_subsystem acpi_configfs = {
248	.su_group = {
249		.cg_item = {
250			.ci_namebuf = "acpi",
251			.ci_type = &acpi_root_group_type,
252		},
253	},
254	.su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex),
255};
256
257static int __init acpi_configfs_init(void)
258{
259	int ret;
260	struct config_group *root = &acpi_configfs.su_group;
261
262	config_group_init(root);
263
264	ret = configfs_register_subsystem(&acpi_configfs);
265	if (ret)
266		return ret;
267
268	acpi_table_group = configfs_register_default_group(root, "table",
269							   &acpi_tables_type);
270	return PTR_ERR_OR_ZERO(acpi_table_group);
271}
272module_init(acpi_configfs_init);
273
274static void __exit acpi_configfs_exit(void)
275{
276	configfs_unregister_default_group(acpi_table_group);
277	configfs_unregister_subsystem(&acpi_configfs);
278}
279module_exit(acpi_configfs_exit);
280
281MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>");
282MODULE_DESCRIPTION("ACPI configfs support");
283MODULE_LICENSE("GPL v2");