Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Originally from efivars.c,
  4 *
  5 * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com>
  6 * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com>
  7 *
  8 * This code takes all variables accessible from EFI runtime and
  9 *  exports them via sysfs
 10 */
 11
 12#include <linux/efi.h>
 13#include <linux/module.h>
 14#include <linux/slab.h>
 15#include <linux/ucs2_string.h>
 16#include <linux/compat.h>
 17
 18#define EFIVARS_VERSION "0.08"
 19#define EFIVARS_DATE "2004-May-17"
 20
 21MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
 22MODULE_DESCRIPTION("sysfs interface to EFI Variables");
 23MODULE_LICENSE("GPL");
 24MODULE_VERSION(EFIVARS_VERSION);
 25MODULE_ALIAS("platform:efivars");
 26
 27LIST_HEAD(efivar_sysfs_list);
 28EXPORT_SYMBOL_GPL(efivar_sysfs_list);
 29
 30static struct kset *efivars_kset;
 31
 32static struct bin_attribute *efivars_new_var;
 33static struct bin_attribute *efivars_del_var;
 34
 35struct compat_efi_variable {
 36	efi_char16_t  VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
 37	efi_guid_t    VendorGuid;
 38	__u32         DataSize;
 39	__u8          Data[1024];
 40	__u32         Status;
 41	__u32         Attributes;
 42} __packed;
 43
 44struct efivar_attribute {
 45	struct attribute attr;
 46	ssize_t (*show) (struct efivar_entry *entry, char *buf);
 47	ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
 48};
 49
 50#define EFIVAR_ATTR(_name, _mode, _show, _store) \
 51struct efivar_attribute efivar_attr_##_name = { \
 52	.attr = {.name = __stringify(_name), .mode = _mode}, \
 53	.show = _show, \
 54	.store = _store, \
 55};
 56
 57#define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr)
 58#define to_efivar_entry(obj)  container_of(obj, struct efivar_entry, kobj)
 59
 60/*
 61 * Prototype for sysfs creation function
 62 */
 63static int
 64efivar_create_sysfs_entry(struct efivar_entry *new_var);
 65
 66static ssize_t
 67efivar_guid_read(struct efivar_entry *entry, char *buf)
 68{
 69	struct efi_variable *var = &entry->var;
 70	char *str = buf;
 71
 72	if (!entry || !buf)
 73		return 0;
 74
 75	efi_guid_to_str(&var->VendorGuid, str);
 76	str += strlen(str);
 77	str += sprintf(str, "\n");
 78
 79	return str - buf;
 80}
 81
 82static ssize_t
 83efivar_attr_read(struct efivar_entry *entry, char *buf)
 84{
 85	struct efi_variable *var = &entry->var;
 86	char *str = buf;
 87
 88	if (!entry || !buf)
 89		return -EINVAL;
 90
 91	var->DataSize = 1024;
 92	if (efivar_entry_get(entry, &var->Attributes, &var->DataSize, var->Data))
 93		return -EIO;
 94
 95	if (var->Attributes & EFI_VARIABLE_NON_VOLATILE)
 96		str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n");
 97	if (var->Attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)
 98		str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n");
 99	if (var->Attributes & EFI_VARIABLE_RUNTIME_ACCESS)
100		str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n");
101	if (var->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD)
102		str += sprintf(str, "EFI_VARIABLE_HARDWARE_ERROR_RECORD\n");
103	if (var->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
104		str += sprintf(str,
105			"EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\n");
106	if (var->Attributes &
107			EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
108		str += sprintf(str,
109			"EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\n");
110	if (var->Attributes & EFI_VARIABLE_APPEND_WRITE)
111		str += sprintf(str, "EFI_VARIABLE_APPEND_WRITE\n");
112	return str - buf;
113}
114
115static ssize_t
116efivar_size_read(struct efivar_entry *entry, char *buf)
117{
118	struct efi_variable *var = &entry->var;
119	char *str = buf;
120
121	if (!entry || !buf)
122		return -EINVAL;
123
124	var->DataSize = 1024;
125	if (efivar_entry_get(entry, &var->Attributes, &var->DataSize, var->Data))
126		return -EIO;
127
128	str += sprintf(str, "0x%lx\n", var->DataSize);
129	return str - buf;
130}
131
132static ssize_t
133efivar_data_read(struct efivar_entry *entry, char *buf)
134{
135	struct efi_variable *var = &entry->var;
136
137	if (!entry || !buf)
138		return -EINVAL;
139
140	var->DataSize = 1024;
141	if (efivar_entry_get(entry, &var->Attributes, &var->DataSize, var->Data))
142		return -EIO;
143
144	memcpy(buf, var->Data, var->DataSize);
145	return var->DataSize;
146}
147
148static inline int
149sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor,
150	     unsigned long size, u32 attributes, u8 *data)
151{
152	/*
153	 * If only updating the variable data, then the name
154	 * and guid should remain the same
155	 */
156	if (memcmp(name, var->VariableName, sizeof(var->VariableName)) ||
157		efi_guidcmp(vendor, var->VendorGuid)) {
158		printk(KERN_ERR "efivars: Cannot edit the wrong variable!\n");
159		return -EINVAL;
160	}
161
162	if ((size <= 0) || (attributes == 0)){
163		printk(KERN_ERR "efivars: DataSize & Attributes must be valid!\n");
164		return -EINVAL;
165	}
166
167	if ((attributes & ~EFI_VARIABLE_MASK) != 0 ||
168	    efivar_validate(vendor, name, data, size) == false) {
169		printk(KERN_ERR "efivars: Malformed variable content\n");
170		return -EINVAL;
171	}
172
173	return 0;
174}
175
176static void
177copy_out_compat(struct efi_variable *dst, struct compat_efi_variable *src)
178{
179	memcpy(dst->VariableName, src->VariableName, EFI_VAR_NAME_LEN);
180	memcpy(dst->Data, src->Data, sizeof(src->Data));
181
182	dst->VendorGuid = src->VendorGuid;
183	dst->DataSize = src->DataSize;
184	dst->Attributes = src->Attributes;
185}
186
187/*
188 * We allow each variable to be edited via rewriting the
189 * entire efi variable structure.
190 */
191static ssize_t
192efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
193{
194	struct efi_variable *new_var, *var = &entry->var;
195	efi_char16_t *name;
196	unsigned long size;
197	efi_guid_t vendor;
198	u32 attributes;
199	u8 *data;
200	int err;
201
202	if (in_compat_syscall()) {
203		struct compat_efi_variable *compat;
204
205		if (count != sizeof(*compat))
206			return -EINVAL;
207
208		compat = (struct compat_efi_variable *)buf;
209		attributes = compat->Attributes;
210		vendor = compat->VendorGuid;
211		name = compat->VariableName;
212		size = compat->DataSize;
213		data = compat->Data;
214
215		err = sanity_check(var, name, vendor, size, attributes, data);
216		if (err)
217			return err;
218
219		copy_out_compat(&entry->var, compat);
220	} else {
221		if (count != sizeof(struct efi_variable))
222			return -EINVAL;
223
224		new_var = (struct efi_variable *)buf;
225
226		attributes = new_var->Attributes;
227		vendor = new_var->VendorGuid;
228		name = new_var->VariableName;
229		size = new_var->DataSize;
230		data = new_var->Data;
231
232		err = sanity_check(var, name, vendor, size, attributes, data);
233		if (err)
234			return err;
235
236		memcpy(&entry->var, new_var, count);
237	}
238
239	err = efivar_entry_set(entry, attributes, size, data, NULL);
240	if (err) {
241		printk(KERN_WARNING "efivars: set_variable() failed: status=%d\n", err);
242		return -EIO;
243	}
244
245	return count;
246}
247
248static ssize_t
249efivar_show_raw(struct efivar_entry *entry, char *buf)
250{
251	struct efi_variable *var = &entry->var;
252	struct compat_efi_variable *compat;
253	size_t size;
254
255	if (!entry || !buf)
256		return 0;
257
258	var->DataSize = 1024;
259	if (efivar_entry_get(entry, &entry->var.Attributes,
260			     &entry->var.DataSize, entry->var.Data))
261		return -EIO;
262
263	if (in_compat_syscall()) {
264		compat = (struct compat_efi_variable *)buf;
265
266		size = sizeof(*compat);
267		memcpy(compat->VariableName, var->VariableName,
268			EFI_VAR_NAME_LEN);
269		memcpy(compat->Data, var->Data, sizeof(compat->Data));
270
271		compat->VendorGuid = var->VendorGuid;
272		compat->DataSize = var->DataSize;
273		compat->Attributes = var->Attributes;
274	} else {
275		size = sizeof(*var);
276		memcpy(buf, var, size);
277	}
278
279	return size;
280}
281
282/*
283 * Generic read/write functions that call the specific functions of
284 * the attributes...
285 */
286static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr,
287				char *buf)
288{
289	struct efivar_entry *var = to_efivar_entry(kobj);
290	struct efivar_attribute *efivar_attr = to_efivar_attr(attr);
291	ssize_t ret = -EIO;
292
293	if (!capable(CAP_SYS_ADMIN))
294		return -EACCES;
295
296	if (efivar_attr->show) {
297		ret = efivar_attr->show(var, buf);
298	}
299	return ret;
300}
301
302static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr,
303				const char *buf, size_t count)
304{
305	struct efivar_entry *var = to_efivar_entry(kobj);
306	struct efivar_attribute *efivar_attr = to_efivar_attr(attr);
307	ssize_t ret = -EIO;
308
309	if (!capable(CAP_SYS_ADMIN))
310		return -EACCES;
311
312	if (efivar_attr->store)
313		ret = efivar_attr->store(var, buf, count);
314
315	return ret;
316}
317
318static const struct sysfs_ops efivar_attr_ops = {
319	.show = efivar_attr_show,
320	.store = efivar_attr_store,
321};
322
323static void efivar_release(struct kobject *kobj)
324{
325	struct efivar_entry *var = to_efivar_entry(kobj);
326	kfree(var);
327}
328
329static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL);
330static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL);
331static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL);
332static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL);
333static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw);
334
335static struct attribute *def_attrs[] = {
336	&efivar_attr_guid.attr,
337	&efivar_attr_size.attr,
338	&efivar_attr_attributes.attr,
339	&efivar_attr_data.attr,
340	&efivar_attr_raw_var.attr,
341	NULL,
342};
343
344static struct kobj_type efivar_ktype = {
345	.release = efivar_release,
346	.sysfs_ops = &efivar_attr_ops,
347	.default_attrs = def_attrs,
348};
349
350static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
351			     struct bin_attribute *bin_attr,
352			     char *buf, loff_t pos, size_t count)
353{
354	struct compat_efi_variable *compat = (struct compat_efi_variable *)buf;
355	struct efi_variable *new_var = (struct efi_variable *)buf;
356	struct efivar_entry *new_entry;
357	bool need_compat = in_compat_syscall();
358	efi_char16_t *name;
359	unsigned long size;
360	u32 attributes;
361	u8 *data;
362	int err;
363
364	if (!capable(CAP_SYS_ADMIN))
365		return -EACCES;
366
367	if (need_compat) {
368		if (count != sizeof(*compat))
369			return -EINVAL;
370
371		attributes = compat->Attributes;
372		name = compat->VariableName;
373		size = compat->DataSize;
374		data = compat->Data;
375	} else {
376		if (count != sizeof(*new_var))
377			return -EINVAL;
378
379		attributes = new_var->Attributes;
380		name = new_var->VariableName;
381		size = new_var->DataSize;
382		data = new_var->Data;
383	}
384
385	if ((attributes & ~EFI_VARIABLE_MASK) != 0 ||
386	    efivar_validate(new_var->VendorGuid, name, data,
387			    size) == false) {
388		printk(KERN_ERR "efivars: Malformed variable content\n");
389		return -EINVAL;
390	}
391
392	new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
393	if (!new_entry)
394		return -ENOMEM;
395
396	if (need_compat)
397		copy_out_compat(&new_entry->var, compat);
398	else
399		memcpy(&new_entry->var, new_var, sizeof(*new_var));
400
401	err = efivar_entry_set(new_entry, attributes, size,
402			       data, &efivar_sysfs_list);
403	if (err) {
404		if (err == -EEXIST)
405			err = -EINVAL;
406		goto out;
407	}
408
409	if (efivar_create_sysfs_entry(new_entry)) {
410		printk(KERN_WARNING "efivars: failed to create sysfs entry.\n");
411		kfree(new_entry);
412	}
413	return count;
414
415out:
416	kfree(new_entry);
417	return err;
418}
419
420static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
421			     struct bin_attribute *bin_attr,
422			     char *buf, loff_t pos, size_t count)
423{
424	struct efi_variable *del_var = (struct efi_variable *)buf;
425	struct compat_efi_variable *compat;
426	struct efivar_entry *entry;
427	efi_char16_t *name;
428	efi_guid_t vendor;
429	int err = 0;
430
431	if (!capable(CAP_SYS_ADMIN))
432		return -EACCES;
433
434	if (in_compat_syscall()) {
435		if (count != sizeof(*compat))
436			return -EINVAL;
437
438		compat = (struct compat_efi_variable *)buf;
439		name = compat->VariableName;
440		vendor = compat->VendorGuid;
441	} else {
442		if (count != sizeof(*del_var))
443			return -EINVAL;
444
445		name = del_var->VariableName;
446		vendor = del_var->VendorGuid;
447	}
448
449	if (efivar_entry_iter_begin())
450		return -EINTR;
451	entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);
452	if (!entry)
453		err = -EINVAL;
454	else if (__efivar_entry_delete(entry))
455		err = -EIO;
456
457	if (err) {
458		efivar_entry_iter_end();
459		return err;
460	}
461
462	if (!entry->scanning) {
463		efivar_entry_iter_end();
464		efivar_unregister(entry);
465	} else
466		efivar_entry_iter_end();
467
468	/* It's dead Jim.... */
469	return count;
470}
471
472/**
473 * efivar_create_sysfs_entry - create a new entry in sysfs
474 * @new_var: efivar entry to create
475 *
476 * Returns 0 on success, negative error code on failure
477 */
478static int
479efivar_create_sysfs_entry(struct efivar_entry *new_var)
480{
481	int short_name_size;
482	char *short_name;
483	unsigned long utf8_name_size;
484	efi_char16_t *variable_name = new_var->var.VariableName;
485	int ret;
486
487	/*
488	 * Length of the variable bytes in UTF8, plus the '-' separator,
489	 * plus the GUID, plus trailing NUL
490	 */
491	utf8_name_size = ucs2_utf8size(variable_name);
492	short_name_size = utf8_name_size + 1 + EFI_VARIABLE_GUID_LEN + 1;
493
494	short_name = kmalloc(short_name_size, GFP_KERNEL);
495	if (!short_name)
496		return -ENOMEM;
497
498	ucs2_as_utf8(short_name, variable_name, short_name_size);
499
500	/* This is ugly, but necessary to separate one vendor's
501	   private variables from another's.         */
502	short_name[utf8_name_size] = '-';
503	efi_guid_to_str(&new_var->var.VendorGuid,
504			 short_name + utf8_name_size + 1);
505
506	new_var->kobj.kset = efivars_kset;
507
508	ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
509				   NULL, "%s", short_name);
510	kfree(short_name);
511	if (ret)
512		return ret;
513
514	kobject_uevent(&new_var->kobj, KOBJ_ADD);
515	if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
516		efivar_unregister(new_var);
517		return -EINTR;
518	}
519
520	return 0;
521}
522
523static int
524create_efivars_bin_attributes(void)
525{
526	struct bin_attribute *attr;
527	int error;
528
529	/* new_var */
530	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
531	if (!attr)
532		return -ENOMEM;
533
534	attr->attr.name = "new_var";
535	attr->attr.mode = 0200;
536	attr->write = efivar_create;
537	efivars_new_var = attr;
538
539	/* del_var */
540	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
541	if (!attr) {
542		error = -ENOMEM;
543		goto out_free;
544	}
545	attr->attr.name = "del_var";
546	attr->attr.mode = 0200;
547	attr->write = efivar_delete;
548	efivars_del_var = attr;
549
550	sysfs_bin_attr_init(efivars_new_var);
551	sysfs_bin_attr_init(efivars_del_var);
552
553	/* Register */
554	error = sysfs_create_bin_file(&efivars_kset->kobj, efivars_new_var);
555	if (error) {
556		printk(KERN_ERR "efivars: unable to create new_var sysfs file"
557			" due to error %d\n", error);
558		goto out_free;
559	}
560
561	error = sysfs_create_bin_file(&efivars_kset->kobj, efivars_del_var);
562	if (error) {
563		printk(KERN_ERR "efivars: unable to create del_var sysfs file"
564			" due to error %d\n", error);
565		sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
566		goto out_free;
567	}
568
569	return 0;
570out_free:
571	kfree(efivars_del_var);
572	efivars_del_var = NULL;
573	kfree(efivars_new_var);
574	efivars_new_var = NULL;
575	return error;
576}
577
578static int efivar_update_sysfs_entry(efi_char16_t *name, efi_guid_t vendor,
579				     unsigned long name_size, void *data)
580{
581	struct efivar_entry *entry = data;
582
583	if (efivar_entry_find(name, vendor, &efivar_sysfs_list, false))
584		return 0;
585
586	memcpy(entry->var.VariableName, name, name_size);
587	memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
588
589	return 1;
590}
591
592static void efivar_update_sysfs_entries(struct work_struct *work)
593{
594	struct efivar_entry *entry;
595	int err;
596
597	/* Add new sysfs entries */
598	while (1) {
599		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
600		if (!entry)
601			return;
602
603		err = efivar_init(efivar_update_sysfs_entry, entry,
604				  false, &efivar_sysfs_list);
605		if (!err)
606			break;
607
608		efivar_create_sysfs_entry(entry);
609	}
610
611	kfree(entry);
612}
613
614static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor,
615				  unsigned long name_size, void *data)
616{
617	struct efivar_entry *entry;
618
619	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
620	if (!entry)
621		return -ENOMEM;
622
623	memcpy(entry->var.VariableName, name, name_size);
624	memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
625
626	efivar_create_sysfs_entry(entry);
627
628	return 0;
629}
630
631static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
632{
633	int err = efivar_entry_remove(entry);
634
635	if (err)
636		return err;
637	efivar_unregister(entry);
638	return 0;
639}
640
641static void efivars_sysfs_exit(void)
642{
643	/* Remove all entries and destroy */
644	int err;
645
646	err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list,
647				  NULL, NULL);
648	if (err) {
649		pr_err("efivars: Failed to destroy sysfs entries\n");
650		return;
651	}
652
653	if (efivars_new_var)
654		sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
655	if (efivars_del_var)
656		sysfs_remove_bin_file(&efivars_kset->kobj, efivars_del_var);
657	kfree(efivars_new_var);
658	kfree(efivars_del_var);
659	kset_unregister(efivars_kset);
660}
661
662int efivars_sysfs_init(void)
663{
664	struct kobject *parent_kobj = efivars_kobject();
665	int error = 0;
666
667	if (!efi_enabled(EFI_RUNTIME_SERVICES))
668		return -ENODEV;
669
670	/* No efivars has been registered yet */
671	if (!parent_kobj)
672		return 0;
673
674	printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
675	       EFIVARS_DATE);
676
677	efivars_kset = kset_create_and_add("vars", NULL, parent_kobj);
678	if (!efivars_kset) {
679		printk(KERN_ERR "efivars: Subsystem registration failed.\n");
680		return -ENOMEM;
681	}
682
683	efivar_init(efivars_sysfs_callback, NULL, true, &efivar_sysfs_list);
684
685	error = create_efivars_bin_attributes();
686	if (error) {
687		efivars_sysfs_exit();
688		return error;
689	}
690
691	INIT_WORK(&efivar_work, efivar_update_sysfs_entries);
692
693	return 0;
694}
695EXPORT_SYMBOL_GPL(efivars_sysfs_init);
696
697module_init(efivars_sysfs_init);
698module_exit(efivars_sysfs_exit);