Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git

This commit is contained in:
Stephen Rothwell 2022-06-28 15:41:12 +10:00
commit a3eea72ac0
38 changed files with 1217 additions and 2464 deletions

View File

@ -29,7 +29,7 @@ Mechanics
be selected::
CONFIG_EFI=y
CONFIG_EFI_VARS=y or m # optional
CONFIG_EFIVAR_FS=y or m # optional
- Create a VFAT partition on the disk
- Copy the following to the VFAT partition:

View File

@ -44,7 +44,6 @@ CONFIG_ARM_CPUIDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_KERNEL_MODE_NEON=y
CONFIG_EFI_VARS=m
CONFIG_EFI_CAPSULE_LOADER=m
CONFIG_ARM_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM_NEON=m

View File

@ -350,8 +350,8 @@ void __init arm64_memblock_init(void)
"initrd not fully accessible via the linear mapping -- please check your bootloader ...\n")) {
phys_initrd_size = 0;
} else {
memblock_remove(base, size); /* clear MEMBLOCK_ flags */
memblock_add(base, size);
memblock_clear_nomap(base, size);
memblock_reserve(base, size);
}
}

View File

@ -10,7 +10,6 @@ CONFIG_SMP=y
CONFIG_NR_CPUS=2
CONFIG_PREEMPT=y
CONFIG_IA64_PALINFO=y
CONFIG_EFI_VARS=y
CONFIG_BINFMT_MISC=m
CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_FAN=m

View File

@ -21,7 +21,6 @@ CONFIG_IA64_MCA_RECOVERY=y
CONFIG_IA64_PALINFO=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_EFI_VARS=y
CONFIG_BINFMT_MISC=m
CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_FAN=m

View File

@ -18,7 +18,6 @@ CONFIG_HOTPLUG_CPU=y
CONFIG_SPARSEMEM_MANUAL=y
CONFIG_IA64_MCA_RECOVERY=y
CONFIG_IA64_PALINFO=y
CONFIG_EFI_VARS=y
CONFIG_BINFMT_MISC=m
CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_FAN=m

View File

@ -23,7 +23,6 @@ CONFIG_FORCE_CPEI_RETARGET=y
CONFIG_IA64_MCA_RECOVERY=y
CONFIG_IA64_PALINFO=y
CONFIG_KEXEC=y
CONFIG_EFI_VARS=y
CONFIG_BINFMT_MISC=m
CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_FAN=m

View File

@ -12,7 +12,6 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_IA64_MCA_RECOVERY=y
CONFIG_IA64_PALINFO=y
CONFIG_CRASH_DUMP=y
CONFIG_EFI_VARS=y
CONFIG_BINFMT_MISC=y
CONFIG_HOTPLUG_PCI=y
CONFIG_HOTPLUG_PCI_ACPI=y

View File

@ -135,7 +135,6 @@ CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DEBUG_DEVRES=y
CONFIG_CONNECTOR=y
CONFIG_EFI_VARS=y
CONFIG_EFI_CAPSULE_LOADER=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_VIRTIO_BLK=y

View File

@ -134,7 +134,6 @@ CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DEBUG_DEVRES=y
CONFIG_CONNECTOR=y
CONFIG_EFI_VARS=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_SD=y

View File

@ -383,7 +383,6 @@ static inline bool efi_is_64bit(void)
extern bool efi_reboot_required(void);
extern bool efi_is_table_address(unsigned long phys_addr);
extern void efi_find_mirror(void);
extern void efi_reserve_boot_services(void);
#else
static inline void parse_efi_setup(u64 phys_addr, u32 data_len) {}
@ -395,9 +394,6 @@ static inline bool efi_is_table_address(unsigned long phys_addr)
{
return false;
}
static inline void efi_find_mirror(void)
{
}
static inline void efi_reserve_boot_services(void)
{
}

View File

@ -108,29 +108,6 @@ static int __init setup_add_efi_memmap(char *arg)
}
early_param("add_efi_memmap", setup_add_efi_memmap);
void __init efi_find_mirror(void)
{
efi_memory_desc_t *md;
u64 mirror_size = 0, total_size = 0;
if (!efi_enabled(EFI_MEMMAP))
return;
for_each_efi_memory_desc(md) {
unsigned long long start = md->phys_addr;
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
total_size += size;
if (md->attribute & EFI_MEMORY_MORE_RELIABLE) {
memblock_mark_mirror(start, size);
mirror_size += size;
}
}
if (mirror_size)
pr_info("Memory: %lldM/%lldM mirrored memory\n",
mirror_size>>20, total_size>>20);
}
/*
* Tell the kernel about the EFI memory map. This might include
* more than the max 128 entries that can fit in the passed in e820

View File

@ -2,18 +2,6 @@
menu "EFI (Extensible Firmware Interface) Support"
depends on EFI
config EFI_VARS
tristate "EFI Variable Support via sysfs"
depends on EFI && (X86 || IA64)
default n
help
If you say Y here, you are able to get EFI (Extensible Firmware
Interface) variable information via sysfs. You may read,
write, create, and destroy EFI variables through this interface.
Note that this driver is only retained for compatibility with
legacy users: new users should use the efivarfs filesystem
instead.
config EFI_ESRT
bool
depends on EFI && !IA64
@ -22,6 +10,7 @@ config EFI_ESRT
config EFI_VARS_PSTORE
tristate "Register efivars backend for pstore"
depends on PSTORE
select UCS2_STRING
default y
help
Say Y here to enable use efivars as a backend to pstore. This
@ -145,6 +134,7 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
config EFI_BOOTLOADER_CONTROL
tristate "EFI Bootloader Control"
select UCS2_STRING
default n
help
This module installs a reboot hook, such that if reboot() is

View File

@ -17,7 +17,6 @@ ifneq ($(CONFIG_EFI_CAPSULE_LOADER),)
obj-$(CONFIG_EFI) += capsule.o
endif
obj-$(CONFIG_EFI_PARAMS_FROM_FDT) += fdtparams.o
obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
obj-$(CONFIG_UEFI_CPER) += cper.o

View File

@ -240,6 +240,7 @@ void __init efi_init(void)
* And now, memblock is fully populated, it is time to do capping.
*/
early_init_dt_check_for_usable_mem_range();
efi_find_mirror();
efi_esrt_init();
efi_mokvar_table_init();

View File

@ -6,6 +6,8 @@
#include <linux/slab.h>
#include <linux/ucs2_string.h>
MODULE_IMPORT_NS(EFIVAR);
#define DUMP_NAME_LEN 66
#define EFIVARS_DATA_SIZE_MAX 1024
@ -20,18 +22,25 @@ module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644);
EFI_VARIABLE_BOOTSERVICE_ACCESS | \
EFI_VARIABLE_RUNTIME_ACCESS)
static LIST_HEAD(efi_pstore_list);
static DECLARE_WORK(efivar_work, NULL);
static int efi_pstore_open(struct pstore_info *psi)
{
psi->data = NULL;
int err;
err = efivar_lock();
if (err)
return err;
psi->data = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
if (!psi->data)
return -ENOMEM;
return 0;
}
static int efi_pstore_close(struct pstore_info *psi)
{
psi->data = NULL;
efivar_unlock();
kfree(psi->data);
return 0;
}
@ -40,22 +49,17 @@ static inline u64 generic_id(u64 timestamp, unsigned int part, int count)
return (timestamp * 100 + part) * 1000 + count;
}
static int efi_pstore_read_func(struct efivar_entry *entry,
struct pstore_record *record)
static int efi_pstore_read_func(struct pstore_record *record,
efi_char16_t *varname)
{
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
unsigned long wlen, size = EFIVARS_DATA_SIZE_MAX;
char name[DUMP_NAME_LEN], data_type;
int i;
efi_status_t status;
int cnt;
unsigned int part;
unsigned long size;
u64 time;
if (efi_guidcmp(entry->var.VendorGuid, vendor))
return 0;
for (i = 0; i < DUMP_NAME_LEN; i++)
name[i] = entry->var.VariableName[i];
ucs2_as_utf8(name, varname, DUMP_NAME_LEN);
if (sscanf(name, "dump-type%u-%u-%d-%llu-%c",
&record->type, &part, &cnt, &time, &data_type) == 5) {
@ -95,161 +99,75 @@ static int efi_pstore_read_func(struct efivar_entry *entry,
} else
return 0;
entry->var.DataSize = 1024;
__efivar_entry_get(entry, &entry->var.Attributes,
&entry->var.DataSize, entry->var.Data);
size = entry->var.DataSize;
memcpy(record->buf, entry->var.Data,
(size_t)min_t(unsigned long, EFIVARS_DATA_SIZE_MAX, size));
return size;
}
/**
* efi_pstore_scan_sysfs_enter
* @pos: scanning entry
* @next: next entry
* @head: list head
*/
static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
struct efivar_entry *next,
struct list_head *head)
{
pos->scanning = true;
if (&next->list != head)
next->scanning = true;
}
/**
* __efi_pstore_scan_sysfs_exit
* @entry: deleting entry
* @turn_off_scanning: Check if a scanning flag should be turned off
*/
static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
bool turn_off_scanning)
{
if (entry->deleting) {
list_del(&entry->list);
efivar_entry_iter_end();
kfree(entry);
if (efivar_entry_iter_begin())
return -EINTR;
} else if (turn_off_scanning)
entry->scanning = false;
return 0;
}
/**
* efi_pstore_scan_sysfs_exit
* @pos: scanning entry
* @next: next entry
* @head: list head
* @stop: a flag checking if scanning will stop
*/
static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
struct efivar_entry *next,
struct list_head *head, bool stop)
{
int ret = __efi_pstore_scan_sysfs_exit(pos, true);
if (ret)
return ret;
if (stop)
ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
return ret;
}
/**
* efi_pstore_sysfs_entry_iter
*
* @record: pstore record to pass to callback
*
* You MUST call efivar_entry_iter_begin() before this function, and
* efivar_entry_iter_end() afterwards.
*
*/
static int efi_pstore_sysfs_entry_iter(struct pstore_record *record)
{
struct efivar_entry **pos = (struct efivar_entry **)&record->psi->data;
struct efivar_entry *entry, *n;
struct list_head *head = &efi_pstore_list;
int size = 0;
int ret;
if (!*pos) {
list_for_each_entry_safe(entry, n, head, list) {
efi_pstore_scan_sysfs_enter(entry, n, head);
size = efi_pstore_read_func(entry, record);
ret = efi_pstore_scan_sysfs_exit(entry, n, head,
size < 0);
if (ret)
return ret;
if (size)
break;
}
*pos = n;
return size;
}
list_for_each_entry_safe_from((*pos), n, head, list) {
efi_pstore_scan_sysfs_enter((*pos), n, head);
size = efi_pstore_read_func((*pos), record);
ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
if (ret)
return ret;
if (size)
break;
}
*pos = n;
return size;
}
/**
* efi_pstore_read
*
* This function returns a size of NVRAM entry logged via efi_pstore_write().
* The meaning and behavior of efi_pstore/pstore are as below.
*
* size > 0: Got data of an entry logged via efi_pstore_write() successfully,
* and pstore filesystem will continue reading subsequent entries.
* size == 0: Entry was not logged via efi_pstore_write(),
* and efi_pstore driver will continue reading subsequent entries.
* size < 0: Failed to get data of entry logging via efi_pstore_write(),
* and pstore will stop reading entry.
*/
static ssize_t efi_pstore_read(struct pstore_record *record)
{
ssize_t size;
record->buf = kzalloc(EFIVARS_DATA_SIZE_MAX, GFP_KERNEL);
record->buf = kmalloc(size, GFP_KERNEL);
if (!record->buf)
return -ENOMEM;
if (efivar_entry_iter_begin()) {
size = -EINTR;
goto out;
}
size = efi_pstore_sysfs_entry_iter(record);
efivar_entry_iter_end();
out:
if (size <= 0) {
status = efivar_get_variable(varname, &LINUX_EFI_CRASH_GUID, NULL,
&size, record->buf);
if (status != EFI_SUCCESS) {
kfree(record->buf);
record->buf = NULL;
return -EIO;
}
/*
* Store the name of the variable in the pstore_record priv field, so
* we can reuse it later if we need to delete the EFI variable from the
* variable store.
*/
wlen = (ucs2_strnlen(varname, DUMP_NAME_LEN) + 1) * sizeof(efi_char16_t);
record->priv = kmemdup(varname, wlen, GFP_KERNEL);
if (!record->priv) {
kfree(record->buf);
return -ENOMEM;
}
return size;
}
static ssize_t efi_pstore_read(struct pstore_record *record)
{
efi_char16_t *varname = record->psi->data;
efi_guid_t guid = LINUX_EFI_CRASH_GUID;
unsigned long varname_size;
efi_status_t status;
for (;;) {
varname_size = EFIVARS_DATA_SIZE_MAX;
/*
* If this is the first read() call in the pstore enumeration,
* varname will be the empty string, and the GetNextVariable()
* runtime service call will return the first EFI variable in
* its own enumeration order, ignoring the guid argument.
*
* Subsequent calls to GetNextVariable() must pass the name and
* guid values returned by the previous call, which is why we
* store varname in record->psi->data. Given that we only
* enumerate variables with the efi-pstore GUID, there is no
* need to record the guid return value.
*/
status = efivar_get_next_variable(&varname_size, varname, &guid);
if (status == EFI_NOT_FOUND)
return 0;
if (status != EFI_SUCCESS)
return -EIO;
/* skip variables that don't concern us */
if (efi_guidcmp(guid, LINUX_EFI_CRASH_GUID))
continue;
return efi_pstore_read_func(record, varname);
}
}
static int efi_pstore_write(struct pstore_record *record)
{
char name[DUMP_NAME_LEN];
efi_char16_t efi_name[DUMP_NAME_LEN];
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
int i, ret = 0;
efi_status_t status;
int i;
record->id = generic_id(record->time.tv_sec, record->part,
record->count);
@ -265,88 +183,26 @@ static int efi_pstore_write(struct pstore_record *record)
for (i = 0; i < DUMP_NAME_LEN; i++)
efi_name[i] = name[i];
ret = efivar_entry_set_safe(efi_name, vendor, PSTORE_EFI_ATTRIBUTES,
false, record->size, record->psi->buf);
if (record->reason == KMSG_DUMP_OOPS && try_module_get(THIS_MODULE))
if (!schedule_work(&efivar_work))
module_put(THIS_MODULE);
return ret;
if (efivar_trylock())
return -EBUSY;
status = efivar_set_variable_locked(efi_name, &LINUX_EFI_CRASH_GUID,
PSTORE_EFI_ATTRIBUTES,
record->size, record->psi->buf,
true);
efivar_unlock();
return status == EFI_SUCCESS ? 0 : -EIO;
};
/*
* Clean up an entry with the same name
*/
static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
{
efi_char16_t *efi_name = data;
efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
unsigned long ucs2_len = ucs2_strlen(efi_name);
if (efi_guidcmp(entry->var.VendorGuid, vendor))
return 0;
if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len))
return 0;
if (entry->scanning) {
/*
* Skip deletion because this entry will be deleted
* after scanning is completed.
*/
entry->deleting = true;
} else
list_del(&entry->list);
/* found */
__efivar_entry_delete(entry);
return 1;
}
static int efi_pstore_erase_name(const char *name)
{
struct efivar_entry *entry = NULL;
efi_char16_t efi_name[DUMP_NAME_LEN];
int found, i;
for (i = 0; i < DUMP_NAME_LEN; i++) {
efi_name[i] = name[i];
if (name[i] == '\0')
break;
}
if (efivar_entry_iter_begin())
return -EINTR;
found = __efivar_entry_iter(efi_pstore_erase_func, &efi_pstore_list,
efi_name, &entry);
efivar_entry_iter_end();
if (found && !entry->scanning)
kfree(entry);
return found ? 0 : -ENOENT;
}
static int efi_pstore_erase(struct pstore_record *record)
{
char name[DUMP_NAME_LEN];
int ret;
efi_status_t status;
snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lld",
record->type, record->part, record->count,
(long long)record->time.tv_sec);
ret = efi_pstore_erase_name(name);
if (ret != -ENOENT)
return ret;
status = efivar_set_variable(record->priv, &LINUX_EFI_CRASH_GUID,
PSTORE_EFI_ATTRIBUTES, 0, NULL);
snprintf(name, sizeof(name), "dump-type%u-%u-%lld",
record->type, record->part, (long long)record->time.tv_sec);
ret = efi_pstore_erase_name(name);
return ret;
if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
return -EIO;
return 0;
}
static struct pstore_info efi_pstore_info = {
@ -360,77 +216,14 @@ static struct pstore_info efi_pstore_info = {
.erase = efi_pstore_erase,
};
static int efi_pstore_callback(efi_char16_t *name, efi_guid_t vendor,
unsigned long name_size, void *data)
{
struct efivar_entry *entry;
int ret;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
memcpy(entry->var.VariableName, name, name_size);
entry->var.VendorGuid = vendor;
ret = efivar_entry_add(entry, &efi_pstore_list);
if (ret)
kfree(entry);
return ret;
}
static int efi_pstore_update_entry(efi_char16_t *name, efi_guid_t vendor,
unsigned long name_size, void *data)
{
struct efivar_entry *entry = data;
if (efivar_entry_find(name, vendor, &efi_pstore_list, false))
return 0;
memcpy(entry->var.VariableName, name, name_size);
memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
return 1;
}
static void efi_pstore_update_entries(struct work_struct *work)
{
struct efivar_entry *entry;
int err;
/* Add new sysfs entries */
while (1) {
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return;
err = efivar_init(efi_pstore_update_entry, entry,
false, &efi_pstore_list);
if (!err)
break;
efivar_entry_add(entry, &efi_pstore_list);
}
kfree(entry);
module_put(THIS_MODULE);
}
static __init int efivars_pstore_init(void)
{
int ret;
if (!efivars_kobject() || !efivar_supports_writes())
if (!efivar_supports_writes())
return 0;
if (efivars_pstore_disable)
return 0;
ret = efivar_init(efi_pstore_callback, NULL, true, &efi_pstore_list);
if (ret)
return ret;
efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
if (!efi_pstore_info.buf)
return -ENOMEM;
@ -443,8 +236,6 @@ static __init int efivars_pstore_init(void)
efi_pstore_info.bufsize = 0;
}
INIT_WORK(&efivar_work, efi_pstore_update_entries);
return 0;
}

View File

@ -202,7 +202,7 @@ static void generic_ops_unregister(void)
}
#ifdef CONFIG_EFI_CUSTOM_SSDT_OVERLAYS
#define EFIVAR_SSDT_NAME_MAX 16
#define EFIVAR_SSDT_NAME_MAX 16UL
static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
static int __init efivar_ssdt_setup(char *str)
{
@ -219,83 +219,62 @@ static int __init efivar_ssdt_setup(char *str)
}
__setup("efivar_ssdt=", efivar_ssdt_setup);
static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor,
unsigned long name_size, void *data)
{
struct efivar_entry *entry;
struct list_head *list = data;
char utf8_name[EFIVAR_SSDT_NAME_MAX];
int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size);
ucs2_as_utf8(utf8_name, name, limit - 1);
if (strncmp(utf8_name, efivar_ssdt, limit) != 0)
return 0;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return 0;
memcpy(entry->var.VariableName, name, name_size);
memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
efivar_entry_add(entry, list);
return 0;
}
static __init int efivar_ssdt_load(void)
{
LIST_HEAD(entries);
struct efivar_entry *entry, *aux;
unsigned long size;
void *data;
int ret;
unsigned long name_size = 256;
efi_char16_t *name = NULL;
efi_status_t status;
efi_guid_t guid;
if (!efivar_ssdt[0])
return 0;
ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries);
name = kzalloc(name_size, GFP_KERNEL);
if (!name)
return -ENOMEM;
list_for_each_entry_safe(entry, aux, &entries, list) {
pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt,
&entry->var.VendorGuid);
for (;;) {
char utf8_name[EFIVAR_SSDT_NAME_MAX];
unsigned long data_size = 0;
void *data;
int limit;
list_del(&entry->list);
ret = efivar_entry_size(entry, &size);
if (ret) {
pr_err("failed to get var size\n");
goto free_entry;
status = efi.get_next_variable(&name_size, name, &guid);
if (status == EFI_NOT_FOUND) {
break;
} else if (status == EFI_BUFFER_TOO_SMALL) {
name = krealloc(name, name_size, GFP_KERNEL);
if (!name)
return -ENOMEM;
continue;
}
data = kmalloc(size, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto free_entry;
limit = min(EFIVAR_SSDT_NAME_MAX, name_size);
ucs2_as_utf8(utf8_name, name, limit - 1);
if (strncmp(utf8_name, efivar_ssdt, limit) != 0)
continue;
pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt, &guid);
status = efi.get_variable(name, &guid, NULL, &data_size, NULL);
if (status != EFI_BUFFER_TOO_SMALL || !data_size)
return -EIO;
data = kmalloc(data_size, GFP_KERNEL);
if (!data)
return -ENOMEM;
status = efi.get_variable(name, &guid, NULL, &data_size, data);
if (status == EFI_SUCCESS) {
acpi_status ret = acpi_load_table(data, NULL);
if (ret)
pr_err("failed to load table: %u\n", ret);
} else {
pr_err("failed to get var data: 0x%lx\n", status);
}
ret = efivar_entry_get(entry, NULL, &size, data);
if (ret) {
pr_err("failed to get var data\n");
goto free_data;
}
ret = acpi_load_table(data, NULL);
if (ret) {
pr_err("failed to load table: %d\n", ret);
goto free_data;
}
goto free_entry;
free_data:
kfree(data);
free_entry:
kfree(entry);
}
return ret;
return 0;
}
#else
static inline int efivar_ssdt_load(void) { return 0; }
@ -446,6 +425,29 @@ static int __init efisubsys_init(void)
subsys_initcall(efisubsys_init);
void __init efi_find_mirror(void)
{
efi_memory_desc_t *md;
u64 mirror_size = 0, total_size = 0;
if (!efi_enabled(EFI_MEMMAP))
return;
for_each_efi_memory_desc(md) {
unsigned long long start = md->phys_addr;
unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
total_size += size;
if (md->attribute & EFI_MEMORY_MORE_RELIABLE) {
memblock_mark_mirror(start, size);
mirror_size += size;
}
}
if (mirror_size)
pr_info("Memory: %lldM/%lldM mirrored memory\n",
mirror_size>>20, total_size>>20);
}
/*
* Find the efi memory descriptor for a given physical address. Given a
* physical address, determine if it exists within an EFI Memory Map entry,
@ -897,6 +899,7 @@ int efi_status_to_err(efi_status_t status)
return err;
}
EXPORT_SYMBOL_GPL(efi_status_to_err);
static DEFINE_SPINLOCK(efi_mem_reserve_persistent_lock);
static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init;

View File

@ -10,69 +10,51 @@
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/ucs2_string.h>
static void efibc_str_to_str16(const char *str, efi_char16_t *str16)
#define MAX_DATA_LEN 512
static int efibc_set_variable(efi_char16_t *name, efi_char16_t *value,
unsigned long len)
{
size_t i;
efi_status_t status;
for (i = 0; i < strlen(str); i++)
str16[i] = str[i];
status = efi.set_variable(name, &LINUX_EFI_LOADER_ENTRY_GUID,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS,
len * sizeof(efi_char16_t), value);
str16[i] = '\0';
}
static int efibc_set_variable(const char *name, const char *value)
{
int ret;
efi_guid_t guid = LINUX_EFI_LOADER_ENTRY_GUID;
struct efivar_entry *entry;
size_t size = (strlen(value) + 1) * sizeof(efi_char16_t);
if (size > sizeof(entry->var.Data)) {
pr_err("value is too large (%zu bytes) for '%s' EFI variable\n", size, name);
return -EINVAL;
if (status != EFI_SUCCESS) {
pr_err("failed to set EFI variable: 0x%lx\n", status);
return -EIO;
}
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
pr_err("failed to allocate efivar entry for '%s' EFI variable\n", name);
return -ENOMEM;
}
efibc_str_to_str16(name, entry->var.VariableName);
efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data);
memcpy(&entry->var.VendorGuid, &guid, sizeof(guid));
ret = efivar_entry_set_safe(entry->var.VariableName,
entry->var.VendorGuid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS,
false, size, entry->var.Data);
if (ret)
pr_err("failed to set %s EFI variable: 0x%x\n",
name, ret);
kfree(entry);
return ret;
return 0;
}
static int efibc_reboot_notifier_call(struct notifier_block *notifier,
unsigned long event, void *data)
{
const char *reason = "shutdown";
efi_char16_t *reason = event == SYS_RESTART ? L"reboot"
: L"shutdown";
const u8 *str = data;
efi_char16_t *wdata;
unsigned long l;
int ret;
if (event == SYS_RESTART)
reason = "reboot";
ret = efibc_set_variable("LoaderEntryRebootReason", reason);
ret = efibc_set_variable(L"LoaderEntryRebootReason", reason,
ucs2_strlen(reason));
if (ret || !data)
return NOTIFY_DONE;
efibc_set_variable("LoaderEntryOneShot", (char *)data);
wdata = kmalloc(MAX_DATA_LEN * sizeof(efi_char16_t), GFP_KERNEL);
for (l = 0; l < MAX_DATA_LEN - 1 && str[l] != '\0'; l++)
wdata[l] = str[l];
wdata[l] = L'\0';
efibc_set_variable(L"LoaderEntryOneShot", wdata, l);
kfree(wdata);
return NOTIFY_DONE;
}
@ -84,7 +66,7 @@ static int __init efibc_init(void)
{
int ret;
if (!efivars_kobject() || !efivar_supports_writes())
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
return -ENODEV;
ret = register_reboot_notifier(&efibc_reboot_notifier);

View File

@ -1,671 +0,0 @@
// 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);
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,
};
ATTRIBUTE_GROUPS(def);
static struct kobj_type efivar_ktype = {
.release = efivar_release,
.sysfs_ops = &efivar_attr_ops,
.default_groups = def_groups,
};
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);

View File

@ -59,8 +59,7 @@ static void __init efi_memmap_free(void)
* Depending on whether mm_init() has already been invoked or not,
* either memblock or "normal" page allocation is used.
*
* Returns the physical address of the allocated memory map on
* success, zero on failure.
* Returns zero on success, a negative error code on failure.
*/
int __init efi_memmap_alloc(unsigned int num_entries,
struct efi_memory_map_data *data)

File diff suppressed because it is too large Load Diff

View File

@ -1597,52 +1597,38 @@ static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context)
static int applespi_get_saved_bl_level(struct applespi_data *applespi)
{
struct efivar_entry *efivar_entry;
efi_status_t sts = EFI_NOT_FOUND;
u16 efi_data = 0;
unsigned long efi_data_len;
int sts;
unsigned long efi_data_len = sizeof(efi_data);
efivar_entry = kmalloc(sizeof(*efivar_entry), GFP_KERNEL);
if (!efivar_entry)
return -ENOMEM;
memcpy(efivar_entry->var.VariableName, EFI_BL_LEVEL_NAME,
sizeof(EFI_BL_LEVEL_NAME));
efivar_entry->var.VendorGuid = EFI_BL_LEVEL_GUID;
efi_data_len = sizeof(efi_data);
sts = efivar_entry_get(efivar_entry, NULL, &efi_data_len, &efi_data);
if (sts && sts != -ENOENT)
if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
sts = efi.get_variable(EFI_BL_LEVEL_NAME, &EFI_BL_LEVEL_GUID,
NULL, &efi_data_len, &efi_data);
if (sts != EFI_SUCCESS && sts != EFI_NOT_FOUND)
dev_warn(&applespi->spi->dev,
"Error getting backlight level from EFI vars: %d\n",
"Error getting backlight level from EFI vars: 0x%lx\n",
sts);
kfree(efivar_entry);
return sts ? sts : efi_data;
return sts != EFI_SUCCESS ? -ENODEV : efi_data;
}
static void applespi_save_bl_level(struct applespi_data *applespi,
unsigned int level)
{
efi_guid_t efi_guid;
efi_status_t sts = EFI_UNSUPPORTED;
u32 efi_attr;
unsigned long efi_data_len;
u16 efi_data;
int sts;
/* Save keyboard backlight level */
efi_guid = EFI_BL_LEVEL_GUID;
efi_data = (u16)level;
efi_data_len = sizeof(efi_data);
efi_attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS;
sts = efivar_entry_set_safe((efi_char16_t *)EFI_BL_LEVEL_NAME, efi_guid,
efi_attr, true, efi_data_len, &efi_data);
if (sts)
if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
sts = efi.set_variable(EFI_BL_LEVEL_NAME, &EFI_BL_LEVEL_GUID,
efi_attr, sizeof(efi_data), &efi_data);
if (sts != EFI_SUCCESS)
dev_warn(&applespi->spi->dev,
"Error saving backlight level to EFI vars: %d\n", sts);
"Error saving backlight level to EFI vars: 0x%lx\n", sts);
}
static int applespi_probe(struct spi_device *spi)

View File

@ -459,43 +459,34 @@ static void brcmf_fw_fix_efi_nvram_ccode(char *data, unsigned long data_len)
static u8 *brcmf_fw_nvram_from_efi(size_t *data_len_ret)
{
const u16 name[] = { 'n', 'v', 'r', 'a', 'm', 0 };
struct efivar_entry *nvram_efivar;
efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f,
0x43, 0x26, 0x81, 0x23, 0xd1, 0x13);
unsigned long data_len = 0;
efi_status_t status;
u8 *data = NULL;
int err;
nvram_efivar = kzalloc(sizeof(*nvram_efivar), GFP_KERNEL);
if (!nvram_efivar)
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return NULL;
memcpy(&nvram_efivar->var.VariableName, name, sizeof(name));
nvram_efivar->var.VendorGuid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61,
0xb5, 0x1f, 0x43, 0x26,
0x81, 0x23, 0xd1, 0x13);
err = efivar_entry_size(nvram_efivar, &data_len);
if (err)
status = efi.get_variable(L"nvram", &guid, NULL, &data_len, NULL);
if (status != EFI_BUFFER_TOO_SMALL)
goto fail;
data = kmalloc(data_len, GFP_KERNEL);
if (!data)
goto fail;
err = efivar_entry_get(nvram_efivar, NULL, &data_len, data);
if (err)
status = efi.get_variable(L"nvram", &guid, NULL, &data_len, data);
if (status != EFI_SUCCESS)
goto fail;
brcmf_fw_fix_efi_nvram_ccode(data, data_len);
brcmf_info("Using nvram EFI variable\n");
kfree(nvram_efivar);
*data_len_ret = data_len;
return data;
fail:
kfree(data);
kfree(nvram_efivar);
return NULL;
}
#else

View File

@ -19,20 +19,14 @@
void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
{
struct efivar_entry *pnvm_efivar;
void *data;
unsigned long package_size;
int err;
efi_status_t status;
*len = 0;
pnvm_efivar = kzalloc(sizeof(*pnvm_efivar), GFP_KERNEL);
if (!pnvm_efivar)
return ERR_PTR(-ENOMEM);
memcpy(&pnvm_efivar->var.VariableName, IWL_UEFI_OEM_PNVM_NAME,
sizeof(IWL_UEFI_OEM_PNVM_NAME));
pnvm_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return ERR_PTR(-ENODEV);
/*
* TODO: we hardcode a maximum length here, because reading
@ -42,27 +36,22 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
package_size = IWL_HARDCODED_PNVM_SIZE;
data = kmalloc(package_size, GFP_KERNEL);
if (!data) {
data = ERR_PTR(-ENOMEM);
goto out;
}
if (!data)
return ERR_PTR(-ENOMEM);
err = efivar_entry_get(pnvm_efivar, NULL, &package_size, data);
if (err) {
status = efi.get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID,
NULL, &package_size, data);
if (status != EFI_SUCCESS) {
IWL_DEBUG_FW(trans,
"PNVM UEFI variable not found %d (len %lu)\n",
err, package_size);
"PNVM UEFI variable not found 0x%lx (len %lu)\n",
status, package_size);
kfree(data);
data = ERR_PTR(err);
goto out;
return ERR_PTR(-ENOENT);
}
IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
*len = package_size;
out:
kfree(pnvm_efivar);
return data;
}
@ -211,21 +200,15 @@ static void *iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
{
struct efivar_entry *reduce_power_efivar;
struct pnvm_sku_package *package;
void *data = NULL;
unsigned long package_size;
int err;
efi_status_t status;
*len = 0;
reduce_power_efivar = kzalloc(sizeof(*reduce_power_efivar), GFP_KERNEL);
if (!reduce_power_efivar)
return ERR_PTR(-ENOMEM);
memcpy(&reduce_power_efivar->var.VariableName, IWL_UEFI_REDUCED_POWER_NAME,
sizeof(IWL_UEFI_REDUCED_POWER_NAME));
reduce_power_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return ERR_PTR(-ENODEV);
/*
* TODO: we hardcode a maximum length here, because reading
@ -235,19 +218,17 @@ void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
package_size = IWL_HARDCODED_REDUCE_POWER_SIZE;
package = kmalloc(package_size, GFP_KERNEL);
if (!package) {
package = ERR_PTR(-ENOMEM);
goto out;
}
if (!package)
return ERR_PTR(-ENOMEM);
err = efivar_entry_get(reduce_power_efivar, NULL, &package_size, package);
if (err) {
status = efi.get_variable(IWL_UEFI_REDUCED_POWER_NAME, &IWL_EFI_VAR_GUID,
NULL, &package_size, data);
if (status != EFI_SUCCESS) {
IWL_DEBUG_FW(trans,
"Reduced Power UEFI variable not found %d (len %lu)\n",
err, package_size);
"Reduced Power UEFI variable not found 0x%lx (len %lu)\n",
status, package_size);
kfree(package);
data = ERR_PTR(err);
goto out;
return ERR_PTR(-ENOENT);
}
IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n",
@ -262,9 +243,6 @@ void *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
kfree(package);
out:
kfree(reduce_power_efivar);
return data;
}
@ -304,22 +282,15 @@ static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
struct iwl_fw_runtime *fwrt)
{
struct efivar_entry *sgom_efivar;
struct uefi_cnv_wlan_sgom_data *data;
unsigned long package_size;
int err, ret;
efi_status_t status;
int ret;
if (!fwrt->geo_enabled)
if (!fwrt->geo_enabled ||
!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return;
sgom_efivar = kzalloc(sizeof(*sgom_efivar), GFP_KERNEL);
if (!sgom_efivar)
return;
memcpy(&sgom_efivar->var.VariableName, IWL_UEFI_SGOM_NAME,
sizeof(IWL_UEFI_SGOM_NAME));
sgom_efivar->var.VendorGuid = IWL_EFI_VAR_GUID;
/* TODO: we hardcode a maximum length here, because reading
* from the UEFI is not working. To implement this properly,
* we have to call efivar_entry_size().
@ -327,15 +298,14 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
package_size = IWL_HARDCODED_SGOM_SIZE;
data = kmalloc(package_size, GFP_KERNEL);
if (!data) {
data = ERR_PTR(-ENOMEM);
goto out;
}
if (!data)
return;
err = efivar_entry_get(sgom_efivar, NULL, &package_size, data);
if (err) {
status = efi.get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID,
NULL, &package_size, data);
if (status != EFI_SUCCESS) {
IWL_DEBUG_FW(trans,
"SGOM UEFI variable not found %d\n", err);
"SGOM UEFI variable not found 0x%lx\n", status);
goto out_free;
}
@ -349,8 +319,6 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
out_free:
kfree(data);
out:
kfree(sgom_efivar);
}
IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
#endif /* CONFIG_ACPI */

View File

@ -1284,7 +1284,7 @@ static int gmin_get_config_var(struct device *maindev,
const struct dmi_system_id *id;
struct device *dev = maindev;
char var8[CFG_VAR_NAME_MAX];
struct efivar_entry *ev;
efi_status_t status;
int i, ret;
/* For sensors, try first to use the _DSM table */
@ -1326,24 +1326,11 @@ static int gmin_get_config_var(struct device *maindev,
for (i = 0; i < sizeof(var8) && var8[i]; i++)
var16[i] = var8[i];
/* Not sure this API usage is kosher; efivar_entry_get()'s
* implementation simply uses VariableName and VendorGuid from
* the struct and ignores the rest, but it seems like there
* ought to be an "official" efivar_entry registered
* somewhere?
*/
ev = kzalloc(sizeof(*ev), GFP_KERNEL);
if (!ev)
return -ENOMEM;
memcpy(&ev->var.VariableName, var16, sizeof(var16));
ev->var.VendorGuid = GMIN_CFG_VAR_EFI_GUID;
ev->var.DataSize = *out_len;
ret = efivar_entry_get(ev, &ev->var.Attributes,
&ev->var.DataSize, ev->var.Data);
if (ret == 0) {
memcpy(out, ev->var.Data, ev->var.DataSize);
*out_len = ev->var.DataSize;
status = EFI_UNSUPPORTED;
if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
status = efi.get_variable(var16, &GMIN_CFG_VAR_EFI_GUID, NULL,
(unsigned long *)out_len, out);
if (status == EFI_SUCCESS) {
dev_info(maindev, "found EFI entry for '%s'\n", var8);
} else if (is_gmin) {
dev_info(maindev, "Failed to find EFI gmin variable %s\n", var8);
@ -1351,8 +1338,6 @@ static int gmin_get_config_var(struct device *maindev,
dev_info(maindev, "Failed to find EFI variable %s\n", var8);
}
kfree(ev);
return ret;
}

View File

@ -5,4 +5,4 @@
obj-$(CONFIG_EFIVAR_FS) += efivarfs.o
efivarfs-objs := inode.o file.o super.o
efivarfs-objs := inode.o file.o super.o vars.o

View File

@ -7,6 +7,46 @@
#define EFIVAR_FS_INTERNAL_H
#include <linux/list.h>
#include <linux/efi.h>
struct efi_variable {
efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
efi_guid_t VendorGuid;
unsigned long DataSize;
__u8 Data[1024];
efi_status_t Status;
__u32 Attributes;
} __attribute__((packed));
struct efivar_entry {
struct efi_variable var;
struct list_head list;
struct kobject kobj;
};
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool duplicates, struct list_head *head);
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
void efivar_entry_remove(struct efivar_entry *entry);
int efivar_entry_delete(struct efivar_entry *entry);
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);
int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data);
int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data);
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set);
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data);
bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
unsigned long data_size);
bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
size_t len);
extern const struct file_operations efivarfs_file_operations;
extern const struct inode_operations efivarfs_dir_inode_operations;

View File

@ -155,10 +155,8 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
goto fail_inode;
}
efivar_entry_size(entry, &size);
err = efivar_entry_add(entry, &efivarfs_list);
if (err)
goto fail_inode;
__efivar_entry_get(entry, NULL, &size, NULL);
__efivar_entry_add(entry, &efivarfs_list);
/* copied by the above to local storage in the dentry. */
kfree(name);
@ -182,10 +180,7 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
static int efivarfs_destroy(struct efivar_entry *entry, void *data)
{
int err = efivar_entry_remove(entry);
if (err)
return err;
efivar_entry_remove(entry);
kfree(entry);
return 0;
}
@ -221,7 +216,7 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
err = efivar_init(efivarfs_callback, (void *)sb, true, &efivarfs_list);
if (err)
__efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
return err;
}
@ -246,7 +241,7 @@ static void efivarfs_kill_sb(struct super_block *sb)
kill_litter_super(sb);
/* Remove all entries and destroy */
__efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL, NULL);
efivar_entry_iter(efivarfs_destroy, &efivarfs_list, NULL);
}
static struct file_system_type efivarfs_type = {

738
fs/efivarfs/vars.c Normal file
View File

@ -0,0 +1,738 @@
// 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>
*/
#include <linux/capability.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/efi.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/ucs2_string.h>
#include "internal.h"
MODULE_IMPORT_NS(EFIVAR);
static bool
validate_device_path(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
struct efi_generic_dev_path *node;
int offset = 0;
node = (struct efi_generic_dev_path *)buffer;
if (len < sizeof(*node))
return false;
while (offset <= len - sizeof(*node) &&
node->length >= sizeof(*node) &&
node->length <= len - offset) {
offset += node->length;
if ((node->type == EFI_DEV_END_PATH ||
node->type == EFI_DEV_END_PATH2) &&
node->sub_type == EFI_DEV_END_ENTIRE)
return true;
node = (struct efi_generic_dev_path *)(buffer + offset);
}
/*
* If we're here then either node->length pointed past the end
* of the buffer or we reached the end of the buffer without
* finding a device path end node.
*/
return false;
}
static bool
validate_boot_order(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
/* An array of 16-bit integers */
if ((len % 2) != 0)
return false;
return true;
}
static bool
validate_load_option(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
u16 filepathlength;
int i, desclength = 0, namelen;
namelen = ucs2_strnlen(var_name, EFI_VAR_NAME_LEN);
/* Either "Boot" or "Driver" followed by four digits of hex */
for (i = match; i < match+4; i++) {
if (var_name[i] > 127 ||
hex_to_bin(var_name[i] & 0xff) < 0)
return true;
}
/* Reject it if there's 4 digits of hex and then further content */
if (namelen > match + 4)
return false;
/* A valid entry must be at least 8 bytes */
if (len < 8)
return false;
filepathlength = buffer[4] | buffer[5] << 8;
/*
* There's no stored length for the description, so it has to be
* found by hand
*/
desclength = ucs2_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2;
/* Each boot entry must have a descriptor */
if (!desclength)
return false;
/*
* If the sum of the length of the description, the claimed filepath
* length and the original header are greater than the length of the
* variable, it's malformed
*/
if ((desclength + filepathlength + 6) > len)
return false;
/*
* And, finally, check the filepath
*/
return validate_device_path(var_name, match, buffer + desclength + 6,
filepathlength);
}
static bool
validate_uint16(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
/* A single 16-bit integer */
if (len != 2)
return false;
return true;
}
static bool
validate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer,
unsigned long len)
{
int i;
for (i = 0; i < len; i++) {
if (buffer[i] > 127)
return false;
if (buffer[i] == 0)
return true;
}
return false;
}
struct variable_validate {
efi_guid_t vendor;
char *name;
bool (*validate)(efi_char16_t *var_name, int match, u8 *data,
unsigned long len);
};
/*
* This is the list of variables we need to validate, as well as the
* whitelist for what we think is safe not to default to immutable.
*
* If it has a validate() method that's not NULL, it'll go into the
* validation routine. If not, it is assumed valid, but still used for
* whitelisting.
*
* Note that it's sorted by {vendor,name}, but globbed names must come after
* any other name with the same prefix.
*/
static const struct variable_validate variable_validate[] = {
{ EFI_GLOBAL_VARIABLE_GUID, "BootNext", validate_uint16 },
{ EFI_GLOBAL_VARIABLE_GUID, "BootOrder", validate_boot_order },
{ EFI_GLOBAL_VARIABLE_GUID, "Boot*", validate_load_option },
{ EFI_GLOBAL_VARIABLE_GUID, "DriverOrder", validate_boot_order },
{ EFI_GLOBAL_VARIABLE_GUID, "Driver*", validate_load_option },
{ EFI_GLOBAL_VARIABLE_GUID, "ConIn", validate_device_path },
{ EFI_GLOBAL_VARIABLE_GUID, "ConInDev", validate_device_path },
{ EFI_GLOBAL_VARIABLE_GUID, "ConOut", validate_device_path },
{ EFI_GLOBAL_VARIABLE_GUID, "ConOutDev", validate_device_path },
{ EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path },
{ EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path },
{ EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string },
{ EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL },
{ EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
{ EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
{ LINUX_EFI_CRASH_GUID, "*", NULL },
{ NULL_GUID, "", NULL },
};
/*
* Check if @var_name matches the pattern given in @match_name.
*
* @var_name: an array of @len non-NUL characters.
* @match_name: a NUL-terminated pattern string, optionally ending in "*". A
* final "*" character matches any trailing characters @var_name,
* including the case when there are none left in @var_name.
* @match: on output, the number of non-wildcard characters in @match_name
* that @var_name matches, regardless of the return value.
* @return: whether @var_name fully matches @match_name.
*/
static bool
variable_matches(const char *var_name, size_t len, const char *match_name,
int *match)
{
for (*match = 0; ; (*match)++) {
char c = match_name[*match];
switch (c) {
case '*':
/* Wildcard in @match_name means we've matched. */
return true;
case '\0':
/* @match_name has ended. Has @var_name too? */
return (*match == len);
default:
/*
* We've reached a non-wildcard char in @match_name.
* Continue only if there's an identical character in
* @var_name.
*/
if (*match < len && c == var_name[*match])
continue;
return false;
}
}
}
bool
efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
unsigned long data_size)
{
int i;
unsigned long utf8_size;
u8 *utf8_name;
utf8_size = ucs2_utf8size(var_name);
utf8_name = kmalloc(utf8_size + 1, GFP_KERNEL);
if (!utf8_name)
return false;
ucs2_as_utf8(utf8_name, var_name, utf8_size);
utf8_name[utf8_size] = '\0';
for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
const char *name = variable_validate[i].name;
int match = 0;
if (efi_guidcmp(vendor, variable_validate[i].vendor))
continue;
if (variable_matches(utf8_name, utf8_size+1, name, &match)) {
if (variable_validate[i].validate == NULL)
break;
kfree(utf8_name);
return variable_validate[i].validate(var_name, match,
data, data_size);
}
}
kfree(utf8_name);
return true;
}
bool
efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
size_t len)
{
int i;
bool found = false;
int match = 0;
/*
* Check if our variable is in the validated variables list
*/
for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
if (efi_guidcmp(variable_validate[i].vendor, vendor))
continue;
if (variable_matches(var_name, len,
variable_validate[i].name, &match)) {
found = true;
break;
}
}
/*
* If it's in our list, it is removable.
*/
return found;
}
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
struct list_head *head)
{
struct efivar_entry *entry, *n;
unsigned long strsize1, strsize2;
bool found = false;
strsize1 = ucs2_strsize(variable_name, 1024);
list_for_each_entry_safe(entry, n, head, list) {
strsize2 = ucs2_strsize(entry->var.VariableName, 1024);
if (strsize1 == strsize2 &&
!memcmp(variable_name, &(entry->var.VariableName),
strsize2) &&
!efi_guidcmp(entry->var.VendorGuid,
*vendor)) {
found = true;
break;
}
}
return found;
}
/*
* Returns the size of variable_name, in bytes, including the
* terminating NULL character, or variable_name_size if no NULL
* character is found among the first variable_name_size bytes.
*/
static unsigned long var_name_strnsize(efi_char16_t *variable_name,
unsigned long variable_name_size)
{
unsigned long len;
efi_char16_t c;
/*
* The variable name is, by definition, a NULL-terminated
* string, so make absolutely sure that variable_name_size is
* the value we expect it to be. If not, return the real size.
*/
for (len = 2; len <= variable_name_size; len += sizeof(c)) {
c = variable_name[(len / sizeof(c)) - 1];
if (!c)
break;
}
return min(len, variable_name_size);
}
/*
* Print a warning when duplicate EFI variables are encountered and
* disable the sysfs workqueue since the firmware is buggy.
*/
static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
unsigned long len16)
{
size_t i, len8 = len16 / sizeof(efi_char16_t);
char *str8;
str8 = kzalloc(len8, GFP_KERNEL);
if (!str8)
return;
for (i = 0; i < len8; i++)
str8[i] = str16[i];
printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n",
str8, vendor_guid);
kfree(str8);
}
/**
* efivar_init - build the initial list of EFI variables
* @func: callback function to invoke for every variable
* @data: function-specific data to pass to @func
* @duplicates: error if we encounter duplicates on @head?
* @head: initialised head of variable list
*
* Get every EFI variable from the firmware and invoke @func. @func
* should call efivar_entry_add() to build the list of variables.
*
* Returns 0 on success, or a kernel error code on failure.
*/
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool duplicates, struct list_head *head)
{
unsigned long variable_name_size = 1024;
efi_char16_t *variable_name;
efi_status_t status;
efi_guid_t vendor_guid;
int err = 0;
variable_name = kzalloc(variable_name_size, GFP_KERNEL);
if (!variable_name) {
printk(KERN_ERR "efivars: Memory allocation failed.\n");
return -ENOMEM;
}
err = efivar_lock();
if (err)
goto free;
/*
* Per EFI spec, the maximum storage allocated for both
* the variable name and variable data is 1024 bytes.
*/
do {
variable_name_size = 1024;
status = efivar_get_next_variable(&variable_name_size,
variable_name,
&vendor_guid);
switch (status) {
case EFI_SUCCESS:
variable_name_size = var_name_strnsize(variable_name,
variable_name_size);
/*
* Some firmware implementations return the
* same variable name on multiple calls to
* get_next_variable(). Terminate the loop
* immediately as there is no guarantee that
* we'll ever see a different variable name,
* and may end up looping here forever.
*/
if (duplicates &&
variable_is_present(variable_name, &vendor_guid,
head)) {
dup_variable_bug(variable_name, &vendor_guid,
variable_name_size);
status = EFI_NOT_FOUND;
} else {
err = func(variable_name, vendor_guid,
variable_name_size, data);
if (err)
status = EFI_NOT_FOUND;
}
break;
case EFI_UNSUPPORTED:
err = -EOPNOTSUPP;
status = EFI_NOT_FOUND;
break;
case EFI_NOT_FOUND:
break;
default:
printk(KERN_WARNING "efivars: get_next_variable: status=%lx\n",
status);
status = EFI_NOT_FOUND;
break;
}
} while (status != EFI_NOT_FOUND);
efivar_unlock();
free:
kfree(variable_name);
return err;
}
/**
* efivar_entry_add - add entry to variable list
* @entry: entry to add to list
* @head: list head
*
* Returns 0 on success, or a kernel error code on failure.
*/
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
{
int err;
err = efivar_lock();
if (err)
return err;
list_add(&entry->list, head);
efivar_unlock();
return 0;
}
/**
* __efivar_entry_add - add entry to variable list
* @entry: entry to add to list
* @head: list head
*/
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
{
list_add(&entry->list, head);
}
/**
* efivar_entry_remove - remove entry from variable list
* @entry: entry to remove from list
*
* Returns 0 on success, or a kernel error code on failure.
*/
void efivar_entry_remove(struct efivar_entry *entry)
{
list_del(&entry->list);
}
/*
* efivar_entry_list_del_unlock - remove entry from variable list
* @entry: entry to remove
*
* Remove @entry from the variable list and release the list lock.
*
* NOTE: slightly weird locking semantics here - we expect to be
* called with the efivars lock already held, and we release it before
* returning. This is because this function is usually called after
* set_variable() while the lock is still held.
*/
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
{
list_del(&entry->list);
efivar_unlock();
}
/**
* efivar_entry_delete - delete variable and remove entry from list
* @entry: entry containing variable to delete
*
* Delete the variable from the firmware and remove @entry from the
* variable list. It is the caller's responsibility to free @entry
* once we return.
*
* Returns 0 on success, -EINTR if we can't grab the semaphore,
* converted EFI status code if set_variable() fails.
*/
int efivar_entry_delete(struct efivar_entry *entry)
{
efi_status_t status;
int err;
err = efivar_lock();
if (err)
return err;
status = efivar_set_variable_locked(entry->var.VariableName,
&entry->var.VendorGuid,
0, 0, NULL, false);
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
efivar_unlock();
return efi_status_to_err(status);
}
efivar_entry_list_del_unlock(entry);
return 0;
}
/**
* efivar_entry_size - obtain the size of a variable
* @entry: entry for this variable
* @size: location to store the variable's size
*/
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size)
{
efi_status_t status;
int err;
*size = 0;
err = efivar_lock();
if (err)
return err;
status = efivar_get_variable(entry->var.VariableName,
&entry->var.VendorGuid, NULL, size, NULL);
efivar_unlock();
if (status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);
return 0;
}
/**
* __efivar_entry_get - call get_variable()
* @entry: read data for this variable
* @attributes: variable attributes
* @size: size of @data buffer
* @data: buffer to store variable data
*
* The caller MUST call efivar_entry_iter_begin() and
* efivar_entry_iter_end() before and after the invocation of this
* function, respectively.
*/
int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data)
{
efi_status_t status;
status = efivar_get_variable(entry->var.VariableName,
&entry->var.VendorGuid,
attributes, size, data);
return efi_status_to_err(status);
}
/**
* efivar_entry_get - call get_variable()
* @entry: read data for this variable
* @attributes: variable attributes
* @size: size of @data buffer
* @data: buffer to store variable data
*/
int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data)
{
int err;
err = efivar_lock();
if (err)
return err;
err = __efivar_entry_get(entry, attributes, size, data);
efivar_unlock();
return 0;
}
/**
* efivar_entry_set_get_size - call set_variable() and get new size (atomic)
* @entry: entry containing variable to set and get
* @attributes: attributes of variable to be written
* @size: size of data buffer
* @data: buffer containing data to write
* @set: did the set_variable() call succeed?
*
* This is a pretty special (complex) function. See efivarfs_file_write().
*
* Atomically call set_variable() for @entry and if the call is
* successful, return the new size of the variable from get_variable()
* in @size. The success of set_variable() is indicated by @set.
*
* Returns 0 on success, -EINVAL if the variable data is invalid,
* -ENOSPC if the firmware does not have enough available space, or a
* converted EFI status code if either of set_variable() or
* get_variable() fail.
*
* If the EFI variable does not exist when calling set_variable()
* (EFI_NOT_FOUND), @entry is removed from the variable list.
*/
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set)
{
efi_char16_t *name = entry->var.VariableName;
efi_guid_t *vendor = &entry->var.VendorGuid;
efi_status_t status;
int err;
*set = false;
if (efivar_validate(*vendor, name, data, *size) == false)
return -EINVAL;
/*
* The lock here protects the get_variable call, the conditional
* set_variable call, and removal of the variable from the efivars
* list (in the case of an authenticated delete).
*/
err = efivar_lock();
if (err)
return err;
/*
* Ensure that the available space hasn't shrunk below the safe level
*/
status = check_var_size(attributes, *size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
if (status != EFI_UNSUPPORTED) {
err = efi_status_to_err(status);
goto out;
}
if (*size > 65536) {
err = -ENOSPC;
goto out;
}
}
status = efivar_set_variable_locked(name, vendor, attributes, *size,
data, false);
if (status != EFI_SUCCESS) {
err = efi_status_to_err(status);
goto out;
}
*set = true;
/*
* Writing to the variable may have caused a change in size (which
* could either be an append or an overwrite), or the variable to be
* deleted. Perform a GetVariable() so we can tell what actually
* happened.
*/
*size = 0;
status = efivar_get_variable(entry->var.VariableName,
&entry->var.VendorGuid,
NULL, size, NULL);
if (status == EFI_NOT_FOUND)
efivar_entry_list_del_unlock(entry);
else
efivar_unlock();
if (status && status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);
return 0;
out:
efivar_unlock();
return err;
}
/**
* efivar_entry_iter - iterate over variable list
* @func: callback function
* @head: head of variable list
* @data: function-specific data to pass to callback
*
* Iterate over the list of EFI variables and call @func with every
* entry on the list. It is safe for @func to remove entries in the
* list via efivar_entry_delete() while iterating.
*
* Some notes for the callback function:
* - a non-zero return value indicates an error and terminates the loop
* - @func is called from atomic context
*/
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data)
{
struct efivar_entry *entry, *n;
int err = 0;
err = efivar_lock();
if (err)
return err;
list_for_each_entry_safe(entry, n, head, list) {
err = func(entry, data);
if (err)
break;
}
efivar_unlock();
return err;
}

View File

@ -55,6 +55,7 @@ static void free_pstore_private(struct pstore_private *private)
return;
if (private->record) {
kfree(private->record->buf);
kfree(private->record->priv);
kfree(private->record);
}
kfree(private);

View File

@ -808,6 +808,7 @@ void pstore_get_backend_records(struct pstore_info *psi,
if (rc) {
/* pstore_mkfile() did not take record, so free it. */
kfree(record->buf);
kfree(record->priv);
kfree(record);
if (rc != -EEXIST || !quiet)
failed++;

View File

@ -872,6 +872,7 @@ static inline bool efi_rt_services_supported(unsigned int mask)
{
return (efi.runtime_supported_mask & mask) == mask;
}
extern void efi_find_mirror(void);
#else
static inline bool efi_enabled(int feature)
{
@ -889,6 +890,8 @@ static inline bool efi_rt_services_supported(unsigned int mask)
{
return false;
}
static inline void efi_find_mirror(void) {}
#endif
extern int efi_status_to_err(efi_status_t status);
@ -1027,29 +1030,6 @@ struct efivars {
#define EFI_VAR_NAME_LEN 1024
struct efi_variable {
efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
efi_guid_t VendorGuid;
unsigned long DataSize;
__u8 Data[1024];
efi_status_t Status;
__u32 Attributes;
} __attribute__((packed));
struct efivar_entry {
struct efi_variable var;
struct list_head list;
struct kobject kobj;
bool scanning;
bool deleting;
};
static inline void
efivar_unregister(struct efivar_entry *var)
{
kobject_put(&var->kobj);
}
int efivars_register(struct efivars *efivars,
const struct efivar_operations *ops,
struct kobject *kobject);
@ -1057,43 +1037,26 @@ int efivars_unregister(struct efivars *efivars);
struct kobject *efivars_kobject(void);
int efivar_supports_writes(void);
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool duplicates, struct list_head *head);
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
int efivar_entry_remove(struct efivar_entry *entry);
int efivar_lock(void);
int efivar_trylock(void);
void efivar_unlock(void);
int __efivar_entry_delete(struct efivar_entry *entry);
int efivar_entry_delete(struct efivar_entry *entry);
efi_status_t efivar_get_variable(efi_char16_t *name, efi_guid_t *vendor,
u32 *attr, unsigned long *size, void *data);
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);
int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data);
int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data);
int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
unsigned long size, void *data, struct list_head *head);
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set);
int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
bool block, unsigned long size, void *data);
efi_status_t efivar_get_next_variable(unsigned long *name_size,
efi_char16_t *name, efi_guid_t *vendor);
int efivar_entry_iter_begin(void);
void efivar_entry_iter_end(void);
efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
u32 attr, unsigned long data_size,
void *data, bool nonblocking);
int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data,
struct efivar_entry **prev);
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data);
efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
u32 attr, unsigned long data_size, void *data);
struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
struct list_head *head, bool remove);
bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
unsigned long data_size);
bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
size_t len);
efi_status_t check_var_size(u32 attributes, unsigned long size);
efi_status_t check_var_size_nonblocking(u32 attributes, unsigned long size);
#if IS_ENABLED(CONFIG_EFI_CAPSULE_LOADER)
extern bool efi_capsule_pending(int *reset_type);

View File

@ -57,6 +57,9 @@ struct pstore_info;
* @size: size of @buf
* @ecc_notice_size:
* ECC information for @buf
* @priv: pointer for backend specific use, will be
* kfree()d by the pstore core if non-NULL
* when the record is freed.
*
* Valid for PSTORE_TYPE_DMESG @type:
*
@ -74,6 +77,7 @@ struct pstore_record {
char *buf;
ssize_t size;
ssize_t ecc_notice_size;
void *priv;
int count;
enum kmsg_dump_reason reason;

View File

@ -861,4 +861,6 @@ struct folio *try_grab_folio(struct page *page, int refs, unsigned int flags);
DECLARE_PER_CPU(struct per_cpu_nodestat, boot_nodestats);
extern bool mirrored_kernelcore;
#endif /* __MM_INTERNAL_H */

View File

@ -327,7 +327,7 @@ static phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start,
NUMA_NO_NODE, flags);
if (!ret && (flags & MEMBLOCK_MIRROR)) {
pr_warn("Could not allocate %pap bytes of mirrored memory\n",
pr_warn_ratelimited("Could not allocate %pap bytes of mirrored memory\n",
&size);
flags &= ~MEMBLOCK_MIRROR;
goto again;
@ -924,6 +924,9 @@ int __init_memblock memblock_clear_hotplug(phys_addr_t base, phys_addr_t size)
*/
int __init_memblock memblock_mark_mirror(phys_addr_t base, phys_addr_t size)
{
if (!mirrored_kernelcore)
return 0;
system_has_some_mirror = true;
return memblock_setclr_flag(base, size, 1, MEMBLOCK_MIRROR);
@ -1384,7 +1387,7 @@ phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,
if (flags & MEMBLOCK_MIRROR) {
flags &= ~MEMBLOCK_MIRROR;
pr_warn("Could not allocate %pap bytes of mirrored memory\n",
pr_warn_ratelimited("Could not allocate %pap bytes of mirrored memory\n",
&size);
goto again;
}

View File

@ -356,7 +356,7 @@ static unsigned long required_kernelcore_percent __initdata;
static unsigned long required_movablecore __initdata;
static unsigned long required_movablecore_percent __initdata;
static unsigned long zone_movable_pfn[MAX_NUMNODES] __initdata;
static bool mirrored_kernelcore __meminitdata;
bool mirrored_kernelcore __initdata_memblock;
/* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */
int movable_zone;

View File

@ -536,7 +536,7 @@ void __meminit vmemmap_verify(pte_t *pte, int node,
int actual_node = early_pfn_to_nid(pfn);
if (node_distance(actual_node, node) > LOCAL_DISTANCE)
pr_warn("[%lx-%lx] potential offnode page_structs\n",
pr_warn_once("[%lx-%lx] potential offnode page_structs\n",
start, end - 1);
}

View File

@ -65,32 +65,6 @@ get_efivarfs_secureboot_mode()
return 0;
}
get_efi_var_secureboot_mode()
{
local efi_vars
local secure_boot_file
local setup_mode_file
local secureboot_mode
local setup_mode
if [ ! -d "$efi_vars" ]; then
log_skip "efi_vars is not enabled\n"
fi
secure_boot_file=$(find "$efi_vars" -name SecureBoot-* 2>/dev/null)
setup_mode_file=$(find "$efi_vars" -name SetupMode-* 2>/dev/null)
if [ -f "$secure_boot_file/data" ] && \
[ -f "$setup_mode_file/data" ]; then
secureboot_mode=`od -An -t u1 "$secure_boot_file/data"`
setup_mode=`od -An -t u1 "$setup_mode_file/data"`
if [ $secureboot_mode -eq 1 ] && [ $setup_mode -eq 0 ]; then
log_info "secure boot mode enabled (CONFIG_EFI_VARS)"
return 1;
fi
fi
return 0;
}
# On powerpc platform, check device-tree property
# /proc/device-tree/ibm,secureboot/os-secureboot-enforcing
# to detect secureboot state.
@ -113,9 +87,8 @@ get_arch()
}
# Check efivar SecureBoot-$(the UUID) and SetupMode-$(the UUID).
# The secure boot mode can be accessed either as the last integer
# of "od -An -t u1 /sys/firmware/efi/efivars/SecureBoot-*" or from
# "od -An -t u1 /sys/firmware/efi/vars/SecureBoot-*/data". The efi
# The secure boot mode can be accessed as the last integer of
# "od -An -t u1 /sys/firmware/efi/efivars/SecureBoot-*". The efi
# SetupMode can be similarly accessed.
# Return 1 for SecureBoot mode enabled and SetupMode mode disabled.
get_secureboot_mode()
@ -129,11 +102,6 @@ get_secureboot_mode()
else
get_efivarfs_secureboot_mode
secureboot_mode=$?
# fallback to using the efi_var files
if [ $secureboot_mode -eq 0 ]; then
get_efi_var_secureboot_mode
secureboot_mode=$?
fi
fi
if [ $secureboot_mode -eq 0 ]; then