Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
5d3c861
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
counter
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
arm_scmi
broadcom
efi
libstub
test
Kconfig
Makefile
apple-properties.c
arm-runtime.c
capsule-loader.c
capsule.c
cper-arm.c
cper-x86.c
cper.c
dev-path-parser.c
earlycon.c
efi-bgrt.c
efi-init.c
efi-pstore.c
efi.c
efibc.c
efivars.c
embedded-firmware.c
esrt.c
fake_mem.c
fake_mem.h
fdtparams.c
memattr.c
memmap.c
mokvar-table.c
rci2-table.c
reboot.c
runtime-map.c
runtime-wrappers.c
tpm.c
vars.c
x86_fake_mem.c
google
imx
meson
psci
smccc
tegra
xilinx
Kconfig
Makefile
arm_scpi.c
arm_sdei.c
dmi-id.c
dmi-sysfs.c
dmi_scan.c
edd.c
iscsi_ibft.c
iscsi_ibft_find.c
memmap.c
pcdp.c
pcdp.h
qcom_scm-legacy.c
qcom_scm-smc.c
qcom_scm.c
qcom_scm.h
qemu_fw_cfg.c
raspberrypi.c
scpi_pm_domain.c
stratix10-rsu.c
stratix10-svc.c
ti_sci.c
ti_sci.h
trusted_foundations.c
turris-mox-rwtm.c
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
ide
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
vdpa
vfio
vhost
video
virt
virtio
visorbus
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
firmware
/
efi
/
efivars.c
Copy path
Blame
Blame
Latest commit
History
History
671 lines (551 loc) · 15.8 KB
Breadcrumbs
linux
/
drivers
/
firmware
/
efi
/
efivars.c
Top
File metadata and controls
Code
Blame
671 lines (551 loc) · 15.8 KB
Raw
// SPDX-License-Identifier: GPL-2.0+ /* * Originally from efivars.c, * * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> * * This code takes all variables accessible from EFI runtime and * exports them via sysfs */ #include <linux/efi.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/ucs2_string.h> #include <linux/compat.h> #define EFIVARS_VERSION "0.08" #define EFIVARS_DATE "2004-May-17" MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); MODULE_DESCRIPTION("sysfs interface to EFI Variables"); MODULE_LICENSE("GPL"); MODULE_VERSION(EFIVARS_VERSION); MODULE_ALIAS("platform:efivars"); static LIST_HEAD(efivar_sysfs_list); static struct kset *efivars_kset; static struct bin_attribute *efivars_new_var; static struct bin_attribute *efivars_del_var; struct compat_efi_variable { efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)]; efi_guid_t VendorGuid; __u32 DataSize; __u8 Data[1024]; __u32 Status; __u32 Attributes; } __packed; struct efivar_attribute { struct attribute attr; ssize_t (*show) (struct efivar_entry *entry, char *buf); ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); }; #define EFIVAR_ATTR(_name, _mode, _show, _store) \ struct efivar_attribute efivar_attr_##_name = { \ .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, \ .store = _store, \ }; #define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr) #define to_efivar_entry(obj) container_of(obj, struct efivar_entry, kobj) /* * Prototype for sysfs creation function */ static int efivar_create_sysfs_entry(struct efivar_entry *new_var); static ssize_t efivar_guid_read(struct efivar_entry *entry, char *buf) { struct efi_variable *var = &entry->var; char *str = buf; if (!entry || !buf) return 0; efi_guid_to_str(&var->VendorGuid, str); str += strlen(str); str += sprintf(str, "\n"); return str - buf; } static ssize_t efivar_attr_read(struct efivar_entry *entry, char *buf) { struct efi_variable *var = &entry->var; unsigned long size = sizeof(var->Data); char *str = buf; int ret; if (!entry || !buf) return -EINVAL; ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); var->DataSize = size; if (ret) return -EIO; if (var->Attributes & EFI_VARIABLE_NON_VOLATILE) str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n"); if (var->Attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS) str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n"); if (var->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n"); if (var->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) str += sprintf(str, "EFI_VARIABLE_HARDWARE_ERROR_RECORD\n"); if (var->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) str += sprintf(str, "EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\n"); if (var->Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) str += sprintf(str, "EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\n"); if (var->Attributes & EFI_VARIABLE_APPEND_WRITE) str += sprintf(str, "EFI_VARIABLE_APPEND_WRITE\n"); return str - buf; } static ssize_t efivar_size_read(struct efivar_entry *entry, char *buf) { struct efi_variable *var = &entry->var; unsigned long size = sizeof(var->Data); char *str = buf; int ret; if (!entry || !buf) return -EINVAL; ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); var->DataSize = size; if (ret) return -EIO; str += sprintf(str, "0x%lx\n", var->DataSize); return str - buf; } static ssize_t efivar_data_read(struct efivar_entry *entry, char *buf) { struct efi_variable *var = &entry->var; unsigned long size = sizeof(var->Data); int ret; if (!entry || !buf) return -EINVAL; ret = efivar_entry_get(entry, &var->Attributes, &size, var->Data); var->DataSize = size; if (ret) return -EIO; memcpy(buf, var->Data, var->DataSize); return var->DataSize; } static inline int sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor, unsigned long size, u32 attributes, u8 *data) { /* * If only updating the variable data, then the name * and guid should remain the same */ if (memcmp(name, var->VariableName, sizeof(var->VariableName)) || efi_guidcmp(vendor, var->VendorGuid)) { printk(KERN_ERR "efivars: Cannot edit the wrong variable!\n"); return -EINVAL; } if ((size <= 0) || (attributes == 0)){ printk(KERN_ERR "efivars: DataSize & Attributes must be valid!\n"); return -EINVAL; } if ((attributes & ~EFI_VARIABLE_MASK) != 0 || efivar_validate(vendor, name, data, size) == false) { printk(KERN_ERR "efivars: Malformed variable content\n"); return -EINVAL; } return 0; } static void copy_out_compat(struct efi_variable *dst, struct compat_efi_variable *src) { memcpy(dst->VariableName, src->VariableName, EFI_VAR_NAME_LEN); memcpy(dst->Data, src->Data, sizeof(src->Data)); dst->VendorGuid = src->VendorGuid; dst->DataSize = src->DataSize; dst->Attributes = src->Attributes; } /* * We allow each variable to be edited via rewriting the * entire efi variable structure. */ static ssize_t efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) { struct efi_variable *new_var, *var = &entry->var; efi_char16_t *name; unsigned long size; efi_guid_t vendor; u32 attributes; u8 *data; int err; if (!entry || !buf) return -EINVAL; if (in_compat_syscall()) { struct compat_efi_variable *compat; if (count != sizeof(*compat)) return -EINVAL; compat = (struct compat_efi_variable *)buf; attributes = compat->Attributes; vendor = compat->VendorGuid; name = compat->VariableName; size = compat->DataSize; data = compat->Data; err = sanity_check(var, name, vendor, size, attributes, data); if (err) return err; copy_out_compat(&entry->var, compat); } else { if (count != sizeof(struct efi_variable)) return -EINVAL; new_var = (struct efi_variable *)buf; attributes = new_var->Attributes; vendor = new_var->VendorGuid; name = new_var->VariableName; size = new_var->DataSize; data = new_var->Data; err = sanity_check(var, name, vendor, size, attributes, data); if (err) return err; memcpy(&entry->var, new_var, count); } err = efivar_entry_set(entry, attributes, size, data, NULL); if (err) { printk(KERN_WARNING "efivars: set_variable() failed: status=%d\n", err); return -EIO; } return count; } static ssize_t efivar_show_raw(struct efivar_entry *entry, char *buf) { struct efi_variable *var = &entry->var; struct compat_efi_variable *compat; unsigned long datasize = sizeof(var->Data); size_t size; int ret; if (!entry || !buf) return 0; ret = efivar_entry_get(entry, &var->Attributes, &datasize, var->Data); var->DataSize = datasize; if (ret) return -EIO; if (in_compat_syscall()) { compat = (struct compat_efi_variable *)buf; size = sizeof(*compat); memcpy(compat->VariableName, var->VariableName, EFI_VAR_NAME_LEN); memcpy(compat->Data, var->Data, sizeof(compat->Data)); compat->VendorGuid = var->VendorGuid; compat->DataSize = var->DataSize; compat->Attributes = var->Attributes; } else { size = sizeof(*var); memcpy(buf, var, size); } return size; } /* * Generic read/write functions that call the specific functions of * the attributes... */ static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct efivar_entry *var = to_efivar_entry(kobj); struct efivar_attribute *efivar_attr = to_efivar_attr(attr); ssize_t ret = -EIO; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (efivar_attr->show) { ret = efivar_attr->show(var, buf); } return ret; } static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct efivar_entry *var = to_efivar_entry(kobj); struct efivar_attribute *efivar_attr = to_efivar_attr(attr); ssize_t ret = -EIO; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (efivar_attr->store) ret = efivar_attr->store(var, buf, count); return ret; } static const struct sysfs_ops efivar_attr_ops = { .show = efivar_attr_show, .store = efivar_attr_store, }; static void efivar_release(struct kobject *kobj) { struct efivar_entry *var = to_efivar_entry(kobj); kfree(var); } static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL); static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL); static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL); static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL); static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw); static struct attribute *def_attrs[] = { &efivar_attr_guid.attr, &efivar_attr_size.attr, &efivar_attr_attributes.attr, &efivar_attr_data.attr, &efivar_attr_raw_var.attr, NULL, }; static struct kobj_type efivar_ktype = { .release = efivar_release, .sysfs_ops = &efivar_attr_ops, .default_attrs = def_attrs, }; static ssize_t efivar_create(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { struct compat_efi_variable *compat = (struct compat_efi_variable *)buf; struct efi_variable *new_var = (struct efi_variable *)buf; struct efivar_entry *new_entry; bool need_compat = in_compat_syscall(); efi_char16_t *name; unsigned long size; u32 attributes; u8 *data; int err; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (need_compat) { if (count != sizeof(*compat)) return -EINVAL; attributes = compat->Attributes; name = compat->VariableName; size = compat->DataSize; data = compat->Data; } else { if (count != sizeof(*new_var)) return -EINVAL; attributes = new_var->Attributes; name = new_var->VariableName; size = new_var->DataSize; data = new_var->Data; } if ((attributes & ~EFI_VARIABLE_MASK) != 0 || efivar_validate(new_var->VendorGuid, name, data, size) == false) { printk(KERN_ERR "efivars: Malformed variable content\n"); return -EINVAL; } new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); if (!new_entry) return -ENOMEM; if (need_compat) copy_out_compat(&new_entry->var, compat); else memcpy(&new_entry->var, new_var, sizeof(*new_var)); err = efivar_entry_set(new_entry, attributes, size, data, &efivar_sysfs_list); if (err) { if (err == -EEXIST) err = -EINVAL; goto out; } if (efivar_create_sysfs_entry(new_entry)) { printk(KERN_WARNING "efivars: failed to create sysfs entry.\n"); kfree(new_entry); } return count; out: kfree(new_entry); return err; } static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { struct efi_variable *del_var = (struct efi_variable *)buf; struct compat_efi_variable *compat; struct efivar_entry *entry; efi_char16_t *name; efi_guid_t vendor; int err = 0; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (in_compat_syscall()) { if (count != sizeof(*compat)) return -EINVAL; compat = (struct compat_efi_variable *)buf; name = compat->VariableName; vendor = compat->VendorGuid; } else { if (count != sizeof(*del_var)) return -EINVAL; name = del_var->VariableName; vendor = del_var->VendorGuid; } if (efivar_entry_iter_begin()) return -EINTR; entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true); if (!entry) err = -EINVAL; else if (__efivar_entry_delete(entry)) err = -EIO; if (err) { efivar_entry_iter_end(); return err; } if (!entry->scanning) { efivar_entry_iter_end(); efivar_unregister(entry); } else efivar_entry_iter_end(); /* It's dead Jim.... */ return count; } /** * efivar_create_sysfs_entry - create a new entry in sysfs * @new_var: efivar entry to create * * Returns 0 on success, negative error code on failure */ static int efivar_create_sysfs_entry(struct efivar_entry *new_var) { int short_name_size; char *short_name; unsigned long utf8_name_size; efi_char16_t *variable_name = new_var->var.VariableName; int ret; /* * Length of the variable bytes in UTF8, plus the '-' separator, * plus the GUID, plus trailing NUL */ utf8_name_size = ucs2_utf8size(variable_name); short_name_size = utf8_name_size + 1 + EFI_VARIABLE_GUID_LEN + 1; short_name = kmalloc(short_name_size, GFP_KERNEL); if (!short_name) return -ENOMEM; ucs2_as_utf8(short_name, variable_name, short_name_size); /* This is ugly, but necessary to separate one vendor's private variables from another's. */ short_name[utf8_name_size] = '-'; efi_guid_to_str(&new_var->var.VendorGuid, short_name + utf8_name_size + 1); new_var->kobj.kset = efivars_kset; ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, NULL, "%s", short_name); kfree(short_name); if (ret) { kobject_put(&new_var->kobj); return ret; } kobject_uevent(&new_var->kobj, KOBJ_ADD); if (efivar_entry_add(new_var, &efivar_sysfs_list)) { efivar_unregister(new_var); return -EINTR; } return 0; } static int create_efivars_bin_attributes(void) { struct bin_attribute *attr; int error; /* new_var */ attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) return -ENOMEM; attr->attr.name = "new_var"; attr->attr.mode = 0200; attr->write = efivar_create; efivars_new_var = attr; /* del_var */ attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) { error = -ENOMEM; goto out_free; } attr->attr.name = "del_var"; attr->attr.mode = 0200; attr->write = efivar_delete; efivars_del_var = attr; sysfs_bin_attr_init(efivars_new_var); sysfs_bin_attr_init(efivars_del_var); /* Register */ error = sysfs_create_bin_file(&efivars_kset->kobj, efivars_new_var); if (error) { printk(KERN_ERR "efivars: unable to create new_var sysfs file" " due to error %d\n", error); goto out_free; } error = sysfs_create_bin_file(&efivars_kset->kobj, efivars_del_var); if (error) { printk(KERN_ERR "efivars: unable to create del_var sysfs file" " due to error %d\n", error); sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); goto out_free; } return 0; out_free: kfree(efivars_del_var); efivars_del_var = NULL; kfree(efivars_new_var); efivars_new_var = NULL; return error; } static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor, unsigned long name_size, void *data) { struct efivar_entry *entry; entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; memcpy(entry->var.VariableName, name, name_size); memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); efivar_create_sysfs_entry(entry); return 0; } static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data) { int err = efivar_entry_remove(entry); if (err) return err; efivar_unregister(entry); return 0; } static void efivars_sysfs_exit(void) { /* Remove all entries and destroy */ int err; err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL); if (err) { pr_err("efivars: Failed to destroy sysfs entries\n"); return; } if (efivars_new_var) sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var); if (efivars_del_var) sysfs_remove_bin_file(&efivars_kset->kobj, efivars_del_var); kfree(efivars_new_var); kfree(efivars_del_var); kset_unregister(efivars_kset); } static int efivars_sysfs_init(void) { struct kobject *parent_kobj = efivars_kobject(); int error = 0; /* No efivars has been registered yet */ if (!parent_kobj || !efivar_supports_writes()) return 0; printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION, EFIVARS_DATE); efivars_kset = kset_create_and_add("vars", NULL, parent_kobj); if (!efivars_kset) { printk(KERN_ERR "efivars: Subsystem registration failed.\n"); return -ENOMEM; } efivar_init(efivars_sysfs_callback, NULL, true, &efivar_sysfs_list); error = create_efivars_bin_attributes(); if (error) { efivars_sysfs_exit(); return error; } return 0; } module_init(efivars_sysfs_init); module_exit(efivars_sysfs_exit);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
You can’t perform that action at this time.