This registers /sys/firmware/efi/{,systab,efivars/} whenever EFI is enabled
and the system is booted with EFI.
This allows
*) userspace to check for the existence of /sys/firmware/efi as a way
to determine whether or it is running on an EFI system.
*) 'mount -t efivarfs none /sys/firmware/efi/efivars' without manually
loading any modules.
v4: rebase on top of the chainsaw branch:
- split into efi.c and vars.c
- move systab from vars.c to efi.c
- address checkpatch warnings
v3: rebase on top of new efi_enabled()
v2: only create /sys/firmware/efi/efivars if the module is being compiled,
and move extern's to efi.h
Cc: Matt Fleming <[email protected]>
Cc: Kay Sievers <[email protected]>
Cc: Jeremy Kerr <[email protected]>
Cc: Matthew Garrett <[email protected]>
Cc: Chun-Yi Lee <[email protected]>
Cc: Andy Whitcroft <[email protected]>
Cc: Tobias Powalowski <[email protected]>
Signed-off-by: Tom Gundersen <[email protected]>
---
drivers/firmware/Makefile | 1 -
drivers/firmware/efi/Makefile | 2 +
drivers/firmware/efi/efi.c | 107 +++++
drivers/firmware/efi/vars.c | 858 +++++++++++++++++++++++++++++++++++++
drivers/firmware/efivars.c | 955 ------------------------------------------
include/linux/efi.h | 2 +
6 files changed, 969 insertions(+), 956 deletions(-)
create mode 100644 drivers/firmware/efi/efi.c
create mode 100644 drivers/firmware/efi/vars.c
delete mode 100644 drivers/firmware/efivars.c
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 31bf68c..299fad6 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -4,7 +4,6 @@
obj-$(CONFIG_DMI) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o
-obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_PCDP) += pcdp.o
obj-$(CONFIG_DELL_RBU) += dell_rbu.o
obj-$(CONFIG_DCDBAS) += dcdbas.o
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index ef5066f..9660b3d 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -1,6 +1,8 @@
#
# Makefile for linux kernel
#
+obj-$(CONFIG_EFI) += efi.o
+obj-$(CONFIG_EFI_VARS) += vars.o
obj-$(CONFIG_EFI_VARS_SYSFS) += sysfs.o
obj-$(CONFIG_EFI_VARS_PSTORE) += pstore.o
obj-$(CONFIG_EFI_VARS_GENERIC_OPS) += generic-ops.o
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
new file mode 100644
index 0000000..0aafa21
--- /dev/null
+++ b/drivers/firmware/efi/efi.c
@@ -0,0 +1,107 @@
+/*
+ * efi.c - EFI subsystem
+ *
+ * Copyright (C) 2001,2003,2004 Dell <[email protected]>
+ * Copyright (C) 2004 Intel Corporation <[email protected]>
+ * Copyright (C) 2013 Tom Gundersen <[email protected]>
+ *
+ * This code registers /sys/firmware/efi{,/efivars} when EFI is supported,
+ * allowing the efivarfs to be mounted or the efivars module to be loaded.
+ * The existance of /sys/firmware/efi may also be used by userspace to
+ * determine that the system supports EFI.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/efi.h>
+
+struct kobject *efi_kobj;
+EXPORT_SYMBOL_GPL(efi_kobj);
+
+struct kobject *efivars_kobj;
+
+/*
+ * Let's not leave out systab information that snuck into
+ * the efivars driver
+ */
+static ssize_t systab_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ char *str = buf;
+
+ if (!kobj || !buf)
+ return -EINVAL;
+
+ if (efi.mps != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "MPS=0x%lx\n", efi.mps);
+ if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
+ if (efi.acpi != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
+ if (efi.smbios != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
+ if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
+ if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
+ if (efi.uga != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "UGA=0x%lx\n", efi.uga);
+
+ return str - buf;
+}
+
+static struct kobj_attribute efi_attr_systab =
+ __ATTR(systab, 0400, systab_show, NULL);
+
+static struct attribute *efi_subsys_attrs[] = {
+ &efi_attr_systab.attr,
+ NULL, /* maybe more in the future? */
+};
+
+static struct attribute_group efi_subsys_attr_group = {
+ .attrs = efi_subsys_attrs,
+};
+
+/*
+ * We register the efi subsystem with the firmware subsystem and the
+ * efivars subsystem with the efi subsystem, if the system was booted with
+ * EFI.
+ */
+static int __init efisubsys_init(void)
+{
+ int error;
+
+ if (!efi_enabled(EFI_BOOT))
+ return 0;
+
+ /* We register the efi directory at /sys/firmware/efi */
+ efi_kobj = kobject_create_and_add("efi", firmware_kobj);
+ if (!efi_kobj) {
+ pr_err("efi: Firmware registration failed.\n");
+ return -ENOMEM;
+ }
+
+ error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
+ if (error) {
+ pr_err("efi: Sysfs attribute export failed with error %d.\n",
+ error);
+ }
+
+#if defined(CONFIG_EFIVAR_FS) || defined(CONFIG_EFIVAR_FS_MODULE)
+ /* and the standard mountpoint for efivarfs */
+ efivars_kobj = kobject_create_and_add("efivars", efi_kobj);
+ if (!efivars_kobj) {
+ pr_err("efivars: Subsystem registration failed.\n");
+ kobject_put(efi_kobj);
+ return -ENOMEM;
+ }
+#endif /* CONFIG_EFIVAR_FS */
+
+ return 0;
+}
+
+subsys_initcall(efisubsys_init);
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
new file mode 100644
index 0000000..1464e56
--- /dev/null
+++ b/drivers/firmware/efi/vars.c
@@ -0,0 +1,858 @@
+/*
+ * EFI Variables - efivars.c
+ *
+ * Copyright (C) 2001,2003,2004 Dell <[email protected]>
+ * Copyright (C) 2004 Intel Corporation <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#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/sysfs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+
+/* Private pointer to registered efivars */
+static struct efivars *__efivars;
+
+static bool
+validate_device_path(struct efi_variable *var, 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(struct efi_variable *var, 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(struct efi_variable *var, int match, u8 *buffer,
+ unsigned long len)
+{
+ u16 filepathlength;
+ int i, desclength = 0, namelen;
+
+ namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName));
+
+ /* Either "Boot" or "Driver" followed by four digits of hex */
+ for (i = match; i < match+4; i++) {
+ if (var->VariableName[i] > 127 ||
+ hex_to_bin(var->VariableName[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 = utf16_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, match, buffer + desclength + 6,
+ filepathlength);
+}
+
+static bool
+validate_uint16(struct efi_variable *var, int match, u8 *buffer,
+ unsigned long len)
+{
+ /* A single 16-bit integer */
+ if (len != 2)
+ return false;
+
+ return true;
+}
+
+static bool
+validate_ascii_string(struct efi_variable *var, 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 {
+ char *name;
+ bool (*validate)(struct efi_variable *var, int match, u8 *data,
+ unsigned long len);
+};
+
+static const struct variable_validate variable_validate[] = {
+ { "BootNext", validate_uint16 },
+ { "BootOrder", validate_boot_order },
+ { "DriverOrder", validate_boot_order },
+ { "Boot*", validate_load_option },
+ { "Driver*", validate_load_option },
+ { "ConIn", validate_device_path },
+ { "ConInDev", validate_device_path },
+ { "ConOut", validate_device_path },
+ { "ConOutDev", validate_device_path },
+ { "ErrOut", validate_device_path },
+ { "ErrOutDev", validate_device_path },
+ { "Timeout", validate_uint16 },
+ { "Lang", validate_ascii_string },
+ { "PlatformLang", validate_ascii_string },
+ { "", NULL },
+};
+
+bool
+efivar_validate(struct efi_variable *var, u8 *data, unsigned long len)
+{
+ int i;
+ u16 *unicode_name = var->VariableName;
+
+ for (i = 0; variable_validate[i].validate != NULL; i++) {
+ const char *name = variable_validate[i].name;
+ int match;
+
+ for (match = 0; ; match++) {
+ char c = name[match];
+ u16 u = unicode_name[match];
+
+ /* All special variables are plain ascii */
+ if (u > 127)
+ return true;
+
+ /* Wildcard in the matching name means we've matched */
+ if (c == '*')
+ return variable_validate[i].validate(var,
+ match, data, len);
+
+ /* Case sensitive match */
+ if (c != u)
+ break;
+
+ /* Reached the end of the string while matching */
+ if (!c)
+ return variable_validate[i].validate(var,
+ match, data, len);
+ }
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(efivar_validate);
+
+static int efi_status_to_err(efi_status_t status)
+{
+ int err;
+
+ switch (status) {
+ case EFI_SUCCESS:
+ err = 0;
+ break;
+ case EFI_INVALID_PARAMETER:
+ err = -EINVAL;
+ break;
+ case EFI_OUT_OF_RESOURCES:
+ err = -ENOSPC;
+ break;
+ case EFI_DEVICE_ERROR:
+ err = -EIO;
+ break;
+ case EFI_WRITE_PROTECTED:
+ err = -EROFS;
+ break;
+ case EFI_SECURITY_VIOLATION:
+ err = -EACCES;
+ break;
+ case EFI_NOT_FOUND:
+ err = -ENOENT;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/**
+ * 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
+ *
+ * Get every EFI variable from the firmware, construct a struct
+ * efivar_entry 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)(struct efivar_entry *, void *), void *data)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ struct efivar_entry *entry;
+ unsigned long variable_name_size = 1024;
+ efi_char16_t *variable_name;
+ efi_status_t status;
+ efi_guid_t vendor_guid;
+
+ variable_name = kzalloc(variable_name_size, GFP_KERNEL);
+ if (!variable_name) {
+ pr_err("efivars: Memory allocation failed.\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Per EFI spec, the maximum storage allocated for both
+ * the variable name and variable data is 1024 bytes.
+ */
+
+ do {
+ variable_name_size = 1024;
+
+ spin_lock(&__efivars->lock);
+ status = ops->get_next_variable(&variable_name_size,
+ variable_name,
+ &vendor_guid);
+ spin_unlock(&__efivars->lock);
+ switch (status) {
+ case EFI_SUCCESS:
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ memcpy(entry->var.VariableName, variable_name,
+ variable_name_size);
+ memcpy(&(entry->var.VendorGuid), &vendor_guid,
+ sizeof(efi_guid_t));
+ func(entry, data);
+
+ break;
+ case EFI_NOT_FOUND:
+ break;
+ default:
+ pr_warn("efivars: get_next_variable: status=%lx\n",
+ status);
+ status = EFI_NOT_FOUND;
+ break;
+ }
+ } while (status != EFI_NOT_FOUND);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(efivar_init);
+
+/**
+ * efivar_entry_add - add entry to variable list
+ * @entry: entry to add to list
+ */
+void efivar_entry_add(struct efivar_entry *entry)
+{
+ spin_lock(&__efivars->lock);
+ list_add(&entry->list, &__efivars->list);
+ spin_unlock(&__efivars->lock);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_add);
+
+/**
+ * efivar_entry_remove - remove entry from variable list
+ * @entry: entry to remove from list
+ */
+void efivar_entry_remove(struct efivar_entry *entry)
+{
+ spin_lock(&__efivars->lock);
+ list_del(&entry->list);
+ spin_unlock(&__efivars->lock);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_remove);
+
+/*
+ * 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)
+{
+ WARN_ON(!spin_is_locked(&__efivars->lock));
+
+ list_del(&entry->list);
+ spin_unlock(&__efivars->lock);
+}
+
+/**
+ * __efivar_entry_delete - delete an EFI variable
+ * @entry: entry containing EFI variable to delete
+ *
+ * Delete the variable from the firmware but leave @entry on the
+ * variable list.
+ *
+ * This function differs from efivar_entry_delete() because it does
+ * not remove @entry from the variable list. Also, it is safe to be
+ * called from within a efivar_entry_iter_begin() and
+ * efivar_entry_iter_end() region, unlike efivar_entry_delete().
+ *
+ * Returns 0 on success, or a converted EFI status code if
+ * set_variable() fails.
+ */
+int __efivar_entry_delete(struct efivar_entry *entry)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+
+ WARN_ON(!spin_is_locked(&__efivars->lock));
+
+ status = ops->set_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ 0, 0, NULL);
+
+ return efi_status_to_err(status);
+}
+EXPORT_SYMBOL_GPL(__efivar_entry_delete);
+
+/**
+ * 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, or a converted EFI status code if
+ * set_variable() fails.
+ */
+int efivar_entry_delete(struct efivar_entry *entry)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+
+ spin_lock(&__efivars->lock);
+ status = ops->set_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ 0, 0, NULL);
+ if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
+ spin_unlock(&__efivars->lock);
+ return efi_status_to_err(status);
+ }
+
+ efivar_entry_list_del_unlock(entry);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(efivar_entry_delete);
+
+/**
+ * efivar_entry_set - call set_variable()
+ * @entry: entry containing the EFI variable to write
+ * @attributes: variable attributes
+ * @size: size of @data buffer
+ * @data: buffer containing variable data
+ * @lookup: check to see if @entry is already on the list?
+ *
+ * Calls set_variable() for an EFI variable. If creating a new EFI
+ * variable, this function is usually followed by efivar_entry_add().
+ *
+ * Returns 0 on success, -EEXIST if @lookup is true and the entry
+ * already exists on the list, or a converted EFI status code if
+ * set_variable() fails.
+ */
+int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
+ unsigned long size, void *data, bool lookup)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+ efi_char16_t *name = entry->var.VariableName;
+ efi_guid_t vendor = entry->var.VendorGuid;
+
+ spin_lock(&__efivars->lock);
+
+ if (lookup && efivar_entry_find(name, vendor, false)) {
+ spin_unlock(&__efivars->lock);
+ return -EEXIST;
+ }
+
+ status = ops->set_variable(name, &vendor, attributes, size, data);
+
+ spin_unlock(&__efivars->lock);
+
+ return efi_status_to_err(status);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_set);
+
+/**
+ * efivar_entry_set_safe - call set_variable() if enough space in firmware
+ * @entry: entry containing the EFI variable to write
+ * @attributes: variable attributes
+ * @size: size of @data buffer
+ * @data: buffer containing variable data
+ * @total: total variable size including variable name
+ *
+ * Calls query_variable_info() to ensure that there is enough free
+ * storage in the firmware for this variable, and if so, calls
+ * set_variable(). If creating a new EFI variable, this function is
+ * usually followed by efivar_entry_add().
+ *
+ * Returns 0 on success, -ENOSPC if the firmware does not have enough
+ * space for set_variable() to succeed, or a converted EFI status code
+ * if set_variable() fails.
+ */
+int efivar_entry_set_safe(struct efivar_entry *entry, u32 attributes,
+ unsigned long size, void *data)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+ efi_char16_t *name = entry->var.VariableName;
+ efi_guid_t vendor = entry->var.VendorGuid;
+ u64 storage_size, remaining_size, max_size;
+ u64 total;
+
+ if (!ops->query_variable_info)
+ return -ENOSYS;
+
+ spin_lock(&__efivars->lock);
+ status = ops->query_variable_info(attributes, &storage_size,
+ &remaining_size, &max_size);
+
+ if (status != EFI_SUCCESS) {
+ spin_unlock(&__efivars->lock);
+ return -ENOSPC;
+ }
+
+ /*
+ * Does the firmware have enough free space to successfully
+ * write this variable?
+ */
+ total = utf16_strsize(name, 1024) + size;
+ if (remaining_size >= total)
+ status = ops->set_variable(name, &vendor,
+ attributes, size, data);
+
+ spin_unlock(&__efivars->lock);
+
+ return efi_status_to_err(status);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_set_safe);
+/**
+ * efivar_entry_find - search for an entry
+ * @name: the EFI variable name
+ * @guid: the EFI variable vendor's guid
+ * @remove: should we remove the entry from the list?
+ *
+ * Search for an entry on the variable list that has the EFI variable
+ * name @name and vendor guid @guid. If an entry is found on the list
+ * and @remove is true, the entry is removed from the list.
+ *
+ * The caller MUST call efivar_entry_iter_begin() and
+ * efivar_entry_iter_end() before and after the invocation of this
+ * function, respectively.
+ *
+ * Returns the entry if found on the list, %NULL otherwise.
+ */
+struct efivar_entry *efivar_entry_find(efi_char16_t *name,
+ efi_guid_t guid, bool remove)
+{
+ struct efivar_entry *entry, *n;
+ int strsize1, strsize2;
+ bool found = false;
+
+ WARN_ON(!spin_is_locked(&__efivars->lock));
+
+ list_for_each_entry_safe(entry, n, &__efivars->list, list) {
+ strsize1 = utf16_strsize(name, 1024);
+ strsize2 = utf16_strsize(entry->var.VariableName, 1024);
+ if (strsize1 == strsize2 &&
+ !memcmp(name, &(entry->var.VariableName), strsize1) &&
+ !efi_guidcmp(guid, entry->var.VendorGuid)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ if (remove)
+ list_del(&entry->list);
+
+ return entry;
+}
+EXPORT_SYMBOL_GPL(efivar_entry_find);
+
+/**
+ * efivar_query_info - call query_variable_info()
+ * @attributes: query for variables with these attributes
+ * @storage_size: maximum space for variables with @attributes
+ * @remaining_size: remaining space for variable with @attributes
+ * @max_size: maximum space for an individual variable with @attributes
+ *
+ * Query the amount of space available in the firmware for variables
+ * with @attributes.
+ *
+ * Returns 0 on success, -ENOSYS if the registered efivars ops do not
+ * support query_variable_info(), or a converted EFI status code if
+ * query_variable_info() fails.
+ */
+int efivar_query_info(u32 attributes, u64 *storage_size,
+ u64 *remaining_size, u64 *max_size)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+
+ if (!ops->query_variable_info)
+ return -ENOSYS;
+
+ spin_lock(&__efivars->lock);
+ status = ops->query_variable_info(attributes, storage_size,
+ remaining_size, max_size);
+ spin_unlock(&__efivars->lock);
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(efivar_query_info);
+
+/**
+ * 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)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+
+ *size = 0;
+
+ spin_lock(&__efivars->lock);
+ status = ops->get_variable(entry->var.VariableName,
+ &entry->var.VendorGuid, NULL, size, NULL);
+ spin_unlock(&__efivars->lock);
+
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return efi_status_to_err(status);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(efivar_entry_size);
+
+/**
+ * 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)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+
+ spin_lock(&__efivars->lock);
+ status = ops->get_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ attributes, size, data);
+ spin_unlock(&__efivars->lock);
+
+ return efi_status_to_err(status);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_get);
+
+/**
+ * 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
+ *
+ * This is a pretty special (complex) function. See efivarfs_file_write().
+ *
+ * Atomically call set_variable() for @entry and if the set 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, 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)
+{
+ const struct efivar_operations *ops = __efivars->ops;
+ efi_status_t status;
+
+ *set = false;
+
+ if (efivar_validate(&entry->var, 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).
+ */
+ spin_lock(&__efivars->lock);
+
+ status = ops->set_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ attributes, *size, data);
+
+ if (status != EFI_SUCCESS) {
+ spin_unlock(&__efivars->lock);
+ return efi_status_to_err(status);
+ }
+
+ /*
+ * 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 = ops->get_variable(entry->var.VariableName,
+ &entry->var.VendorGuid,
+ NULL, size, NULL);
+
+ if (status == EFI_NOT_FOUND)
+ efivar_entry_list_del_unlock(entry);
+ else
+ spin_unlock(&__efivars->lock);
+
+ return efi_status_to_err(status);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_set_get_size);
+
+/**
+ * efivar_entry_iter_begin - begin iterating the variable list
+ *
+ * Lock the variable list to prevent entry insertion and removal until
+ * efivar_entry_iter_end() is called. This function is usually used in
+ * conjunction with __efivar_entry_iter() or efivar_entry_iter().
+ */
+void efivar_entry_iter_begin(void)
+{
+ spin_lock(&__efivars->lock);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
+
+/**
+ * efivar_entry_iter_end - finish iterating the variable list
+ *
+ * Unlock the variable list and allow modifications to the list again.
+ */
+void efivar_entry_iter_end(void)
+{
+ spin_unlock(&__efivars->lock);
+}
+EXPORT_SYMBOL_GPL(efivar_entry_iter_end);
+
+/**
+ * __efivar_entry_iter - iterate over variable list
+ * @func: callback function
+ * @data: function-specific data to pass to callback
+ * @prev: entry to begin iterating from
+ *
+ * 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().
+ *
+ * You MUST call efivar_enter_iter_begin() before this function, and
+ * efivar_entry_iter_end() afterwards.
+ *
+ * It is possible to begin iteration from an arbitrary entry within
+ * the list by passing @prev. @prev is updated on return to point to
+ * the last entry passed to @func. To begin iterating from the
+ * beginning of the list @prev must be %NULL.
+ *
+ * The restrictions for @func are the same as documented for
+ * efivar_entry_iter().
+ */
+int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
+ void *data, struct efivar_entry **prev)
+{
+ struct efivar_entry *entry, *n;
+ int err = 0;
+
+ if (!prev || !*prev) {
+ list_for_each_entry_safe(entry, n, &__efivars->list, list) {
+ err = func(entry, data);
+ if (err)
+ break;
+ }
+
+ if (prev)
+ *prev = entry;
+
+ return err;
+ }
+
+
+ list_for_each_entry_safe_continue((*prev), n, &__efivars->list, list) {
+ err = func(*prev, data);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(__efivar_entry_iter);
+
+/**
+ * efivar_entry_iter - iterate over variable list
+ * @func: callback function
+ * @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 *), void *data)
+{
+ int err = 0;
+
+ efivar_entry_iter_begin();
+ err = __efivar_entry_iter(func, data, NULL);
+ efivar_entry_iter_end();
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(efivar_entry_iter);
+
+/**
+ * efivars_register - register an efivars
+ * @efivars: efivars to register
+ * @ops: efivars operations
+ *
+ * Only a single efivars can be registered at any time.
+ */
+int efivars_register(struct efivars *efivars,
+ const struct efivar_operations *ops)
+{
+ if (__efivars)
+ return -EBUSY;
+
+ spin_lock_init(&efivars->lock);
+ INIT_LIST_HEAD(&efivars->list);
+ efivars->ops = ops;
+
+ __efivars = efivars;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(efivars_register);
+
+/**
+ * efivars_unregister - unregister an efivars
+ * @efivars: efivars to unregister
+ *
+ * The caller must have already removed every entry from the list,
+ * failure to do so is an error.
+ */
+int efivars_unregister(struct efivars *efivars)
+{
+ if (!__efivars) {
+ pr_err("efivars not registered\n");
+ return -EINVAL;
+ }
+
+ if (__efivars != efivars)
+ return -EINVAL;
+
+ if (!list_empty(&efivars->list)) {
+ pr_err("efivars list not empty\n");
+ return -EBUSY;
+ }
+
+ __efivars = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(efivars_unregister);
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
deleted file mode 100644
index 016e51f..0000000
--- a/drivers/firmware/efivars.c
+++ /dev/null
@@ -1,955 +0,0 @@
-/*
- * EFI Variables - efivars.c
- *
- * Copyright (C) 2001,2003,2004 Dell <[email protected]>
- * Copyright (C) 2004 Intel Corporation <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#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/sysfs.h>
-#include <linux/kobject.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/ctype.h>
-
-#include <linux/fs.h>
-#include <linux/ramfs.h>
-#include <linux/pagemap.h>
-
-#include <asm/uaccess.h>
-
-/* Private pointer to registered efivars */
-static struct efivars *__efivars;
-
-static bool
-validate_device_path(struct efi_variable *var, 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(struct efi_variable *var, 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(struct efi_variable *var, int match, u8 *buffer,
- unsigned long len)
-{
- u16 filepathlength;
- int i, desclength = 0, namelen;
-
- namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName));
-
- /* Either "Boot" or "Driver" followed by four digits of hex */
- for (i = match; i < match+4; i++) {
- if (var->VariableName[i] > 127 ||
- hex_to_bin(var->VariableName[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 = utf16_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, match, buffer + desclength + 6,
- filepathlength);
-}
-
-static bool
-validate_uint16(struct efi_variable *var, int match, u8 *buffer,
- unsigned long len)
-{
- /* A single 16-bit integer */
- if (len != 2)
- return false;
-
- return true;
-}
-
-static bool
-validate_ascii_string(struct efi_variable *var, 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 {
- char *name;
- bool (*validate)(struct efi_variable *var, int match, u8 *data,
- unsigned long len);
-};
-
-static const struct variable_validate variable_validate[] = {
- { "BootNext", validate_uint16 },
- { "BootOrder", validate_boot_order },
- { "DriverOrder", validate_boot_order },
- { "Boot*", validate_load_option },
- { "Driver*", validate_load_option },
- { "ConIn", validate_device_path },
- { "ConInDev", validate_device_path },
- { "ConOut", validate_device_path },
- { "ConOutDev", validate_device_path },
- { "ErrOut", validate_device_path },
- { "ErrOutDev", validate_device_path },
- { "Timeout", validate_uint16 },
- { "Lang", validate_ascii_string },
- { "PlatformLang", validate_ascii_string },
- { "", NULL },
-};
-
-bool
-efivar_validate(struct efi_variable *var, u8 *data, unsigned long len)
-{
- int i;
- u16 *unicode_name = var->VariableName;
-
- for (i = 0; variable_validate[i].validate != NULL; i++) {
- const char *name = variable_validate[i].name;
- int match;
-
- for (match = 0; ; match++) {
- char c = name[match];
- u16 u = unicode_name[match];
-
- /* All special variables are plain ascii */
- if (u > 127)
- return true;
-
- /* Wildcard in the matching name means we've matched */
- if (c == '*')
- return variable_validate[i].validate(var,
- match, data, len);
-
- /* Case sensitive match */
- if (c != u)
- break;
-
- /* Reached the end of the string while matching */
- if (!c)
- return variable_validate[i].validate(var,
- match, data, len);
- }
- }
-
- return true;
-}
-EXPORT_SYMBOL_GPL(efivar_validate);
-
-static int efi_status_to_err(efi_status_t status)
-{
- int err;
-
- switch (status) {
- case EFI_SUCCESS:
- err = 0;
- break;
- case EFI_INVALID_PARAMETER:
- err = -EINVAL;
- break;
- case EFI_OUT_OF_RESOURCES:
- err = -ENOSPC;
- break;
- case EFI_DEVICE_ERROR:
- err = -EIO;
- break;
- case EFI_WRITE_PROTECTED:
- err = -EROFS;
- break;
- case EFI_SECURITY_VIOLATION:
- err = -EACCES;
- break;
- case EFI_NOT_FOUND:
- err = -ENOENT;
- break;
- default:
- err = -EINVAL;
- }
-
- return err;
-}
-
-/*
- * Let's not leave out systab information that snuck into
- * the efivars driver
- */
-static ssize_t systab_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
-{
- char *str = buf;
-
- if (!kobj || !buf)
- return -EINVAL;
-
- if (efi.mps != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "MPS=0x%lx\n", efi.mps);
- if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
- if (efi.acpi != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
- if (efi.smbios != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
- if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
- if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info);
- if (efi.uga != EFI_INVALID_TABLE_ADDR)
- str += sprintf(str, "UGA=0x%lx\n", efi.uga);
-
- return str - buf;
-}
-
-static struct kobj_attribute efi_attr_systab =
- __ATTR(systab, 0400, systab_show, NULL);
-
-static struct attribute *efi_subsys_attrs[] = {
- &efi_attr_systab.attr,
- NULL, /* maybe more in the future? */
-};
-
-static struct attribute_group efi_subsys_attr_group = {
- .attrs = efi_subsys_attrs,
-};
-
-struct kobject *efi_kobj;
-
-/**
- * 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
- *
- * Get every EFI variable from the firmware, construct a struct
- * efivar_entry 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)(struct efivar_entry *, void *), void *data)
-{
- const struct efivar_operations *ops = __efivars->ops;
- struct efivar_entry *entry;
- unsigned long variable_name_size = 1024;
- efi_char16_t *variable_name;
- efi_status_t status;
- efi_guid_t vendor_guid;
-
- variable_name = kzalloc(variable_name_size, GFP_KERNEL);
- if (!variable_name) {
- printk(KERN_ERR "efivars: Memory allocation failed.\n");
- return -ENOMEM;
- }
-
- /*
- * Per EFI spec, the maximum storage allocated for both
- * the variable name and variable data is 1024 bytes.
- */
-
- do {
- variable_name_size = 1024;
-
- spin_lock(&__efivars->lock);
- status = ops->get_next_variable(&variable_name_size,
- variable_name,
- &vendor_guid);
- spin_unlock(&__efivars->lock);
- switch (status) {
- case EFI_SUCCESS:
- entry = kmalloc(sizeof(*entry), GFP_KERNEL);
- if (!entry)
- return -ENOMEM;
-
- memcpy(entry->var.VariableName, variable_name,
variable_name_size);
- memcpy(&(entry->var.VendorGuid), &vendor_guid,
sizeof(efi_guid_t));
- func(entry, data);
-
- 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);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(efivar_init);
-
-/**
- * efivar_entry_add - add entry to variable list
- * @entry: entry to add to list
- */
-void efivar_entry_add(struct efivar_entry *entry)
-{
- spin_lock(&__efivars->lock);
- list_add(&entry->list, &__efivars->list);
- spin_unlock(&__efivars->lock);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_add);
-
-/**
- * efivar_entry_remove - remove entry from variable list
- * @entry: entry to remove from list
- */
-void efivar_entry_remove(struct efivar_entry *entry)
-{
- spin_lock(&__efivars->lock);
- list_del(&entry->list);
- spin_unlock(&__efivars->lock);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_remove);
-
-/*
- * 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)
-{
- WARN_ON(!spin_is_locked(&__efivars->lock));
-
- list_del(&entry->list);
- spin_unlock(&__efivars->lock);
-}
-
-/**
- * __efivar_entry_delete - delete an EFI variable
- * @entry: entry containing EFI variable to delete
- *
- * Delete the variable from the firmware but leave @entry on the
- * variable list.
- *
- * This function differs from efivar_entry_delete() because it does
- * not remove @entry from the variable list. Also, it is safe to be
- * called from within a efivar_entry_iter_begin() and
- * efivar_entry_iter_end() region, unlike efivar_entry_delete().
- *
- * Returns 0 on success, or a converted EFI status code if
- * set_variable() fails.
- */
-int __efivar_entry_delete(struct efivar_entry *entry)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
-
- WARN_ON(!spin_is_locked(&__efivars->lock));
-
- status = ops->set_variable(entry->var.VariableName,
- &entry->var.VendorGuid,
- 0, 0, NULL);
-
- return efi_status_to_err(status);
-}
-EXPORT_SYMBOL_GPL(__efivar_entry_delete);
-
-/**
- * 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, or a converted EFI status code if
- * set_variable() fails.
- */
-int efivar_entry_delete(struct efivar_entry *entry)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
-
- spin_lock(&__efivars->lock);
- status = ops->set_variable(entry->var.VariableName,
- &entry->var.VendorGuid,
- 0, 0, NULL);
- if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
- spin_unlock(&__efivars->lock);
- return efi_status_to_err(status);
- }
-
- efivar_entry_list_del_unlock(entry);
- return 0;
-}
-EXPORT_SYMBOL_GPL(efivar_entry_delete);
-
-/**
- * efivar_entry_set - call set_variable()
- * @entry: entry containing the EFI variable to write
- * @attributes: variable attributes
- * @size: size of @data buffer
- * @data: buffer containing variable data
- * @lookup: check to see if @entry is already on the list?
- *
- * Calls set_variable() for an EFI variable. If creating a new EFI
- * variable, this function is usually followed by efivar_entry_add().
- *
- * Returns 0 on success, -EEXIST if @lookup is true and the entry
- * already exists on the list, or a converted EFI status code if
- * set_variable() fails.
- */
-int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
- unsigned long size, void *data, bool lookup)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
- efi_char16_t *name = entry->var.VariableName;
- efi_guid_t vendor = entry->var.VendorGuid;
-
- spin_lock(&__efivars->lock);
-
- if (lookup && efivar_entry_find(name, vendor, false)) {
- spin_unlock(&__efivars->lock);
- return -EEXIST;
- }
-
- status = ops->set_variable(name, &vendor, attributes, size, data);
-
- spin_unlock(&__efivars->lock);
-
- return efi_status_to_err(status);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_set);
-
-/**
- * efivar_entry_set_safe - call set_variable() if enough space in firmware
- * @entry: entry containing the EFI variable to write
- * @attributes: variable attributes
- * @size: size of @data buffer
- * @data: buffer containing variable data
- * @total: total variable size including variable name
- *
- * Calls query_variable_info() to ensure that there is enough free
- * storage in the firmware for this variable, and if so, calls
- * set_variable(). If creating a new EFI variable, this function is
- * usually followed by efivar_entry_add().
- *
- * Returns 0 on success, -ENOSPC if the firmware does not have enough
- * space for set_variable() to succeed, or a converted EFI status code
- * if set_variable() fails.
- */
-int efivar_entry_set_safe(struct efivar_entry *entry, u32 attributes,
- unsigned long size, void *data)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
- efi_char16_t *name = entry->var.VariableName;
- efi_guid_t vendor = entry->var.VendorGuid;
- u64 storage_size, remaining_size, max_size;
- u64 total;
-
- if (!ops->query_variable_info)
- return -ENOSYS;
-
- spin_lock(&__efivars->lock);
- status = ops->query_variable_info(attributes, &storage_size,
- &remaining_size, &max_size);
-
- if (status != EFI_SUCCESS) {
- spin_unlock(&__efivars->lock);
- return -ENOSPC;
- }
-
- /*
- * Does the firmware have enough free space to successfully
- * write this variable?
- */
- total = utf16_strsize(name, 1024) + size;
- if (remaining_size >= total)
- status = ops->set_variable(name, &vendor,
- attributes, size, data);
-
- spin_unlock(&__efivars->lock);
-
- return efi_status_to_err(status);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_set_safe);
-/**
- * efivar_entry_find - search for an entry
- * @name: the EFI variable name
- * @guid: the EFI variable vendor's guid
- * @remove: should we remove the entry from the list?
- *
- * Search for an entry on the variable list that has the EFI variable
- * name @name and vendor guid @guid. If an entry is found on the list
- * and @remove is true, the entry is removed from the list.
- *
- * The caller MUST call efivar_entry_iter_begin() and
- * efivar_entry_iter_end() before and after the invocation of this
- * function, respectively.
- *
- * Returns the entry if found on the list, %NULL otherwise.
- */
-struct efivar_entry *efivar_entry_find(efi_char16_t *name,
- efi_guid_t guid, bool remove)
-{
- struct efivar_entry *entry, *n;
- int strsize1, strsize2;
- bool found = false;
-
- WARN_ON(!spin_is_locked(&__efivars->lock));
-
- list_for_each_entry_safe(entry, n, &__efivars->list, list) {
- strsize1 = utf16_strsize(name, 1024);
- strsize2 = utf16_strsize(entry->var.VariableName, 1024);
- if (strsize1 == strsize2 &&
- !memcmp(name, &(entry->var.VariableName), strsize1) &&
- !efi_guidcmp(guid, entry->var.VendorGuid)) {
- found = true;
- break;
- }
- }
-
- if (!found)
- return NULL;
-
- if (remove)
- list_del(&entry->list);
-
- return entry;
-}
-EXPORT_SYMBOL_GPL(efivar_entry_find);
-
-/**
- * efivar_query_info - call query_variable_info()
- * @attributes: query for variables with these attributes
- * @storage_size: maximum space for variables with @attributes
- * @remaining_size: remaining space for variable with @attributes
- * @max_size: maximum space for an individual variable with @attributes
- *
- * Query the amount of space available in the firmware for variables
- * with @attributes.
- *
- * Returns 0 on success, -ENOSYS if the registered efivars ops do not
- * support query_variable_info(), or a converted EFI status code if
- * query_variable_info() fails.
- */
-int efivar_query_info(u32 attributes, u64 *storage_size,
- u64 *remaining_size, u64 *max_size)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
-
- if (!ops->query_variable_info)
- return -ENOSYS;
-
- spin_lock(&__efivars->lock);
- status = ops->query_variable_info(attributes, storage_size,
- remaining_size, max_size);
- spin_unlock(&__efivars->lock);
-
- return status;
-}
-EXPORT_SYMBOL_GPL(efivar_query_info);
-
-/**
- * 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)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
-
- *size = 0;
-
- spin_lock(&__efivars->lock);
- status = ops->get_variable(entry->var.VariableName,
- &entry->var.VendorGuid, NULL, size, NULL);
- spin_unlock(&__efivars->lock);
-
- if (status != EFI_BUFFER_TOO_SMALL)
- return efi_status_to_err(status);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(efivar_entry_size);
-
-/**
- * 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)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
-
- spin_lock(&__efivars->lock);
- status = ops->get_variable(entry->var.VariableName,
- &entry->var.VendorGuid,
- attributes, size, data);
- spin_unlock(&__efivars->lock);
-
- return efi_status_to_err(status);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_get);
-
-/**
- * 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
- *
- * This is a pretty special (complex) function. See efivarfs_file_write().
- *
- * Atomically call set_variable() for @entry and if the set 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, 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)
-{
- const struct efivar_operations *ops = __efivars->ops;
- efi_status_t status;
-
- *set = false;
-
- if (efivar_validate(&entry->var, 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).
- */
- spin_lock(&__efivars->lock);
-
- status = ops->set_variable(entry->var.VariableName,
- &entry->var.VendorGuid,
- attributes, *size, data);
-
- if (status != EFI_SUCCESS) {
- spin_unlock(&__efivars->lock);
- return efi_status_to_err(status);
- }
-
- /*
- * 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 = ops->get_variable(entry->var.VariableName,
- &entry->var.VendorGuid,
- NULL, size, NULL);
-
- if (status == EFI_NOT_FOUND)
- efivar_entry_list_del_unlock(entry);
- else
- spin_unlock(&__efivars->lock);
-
- return efi_status_to_err(status);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_set_get_size);
-
-/**
- * efivar_entry_iter_begin - begin iterating the variable list
- *
- * Lock the variable list to prevent entry insertion and removal until
- * efivar_entry_iter_end() is called. This function is usually used in
- * conjunction with __efivar_entry_iter() or efivar_entry_iter().
- */
-void efivar_entry_iter_begin(void)
-{
- spin_lock(&__efivars->lock);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
-
-/**
- * efivar_entry_iter_end - finish iterating the variable list
- *
- * Unlock the variable list and allow modifications to the list again.
- */
-void efivar_entry_iter_end(void)
-{
- spin_unlock(&__efivars->lock);
-}
-EXPORT_SYMBOL_GPL(efivar_entry_iter_end);
-
-/**
- * __efivar_entry_iter - iterate over variable list
- * @func: callback function
- * @data: function-specific data to pass to callback
- * @prev: entry to begin iterating from
- *
- * 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().
- *
- * You MUST call efivar_enter_iter_begin() before this function, and
- * efivar_entry_iter_end() afterwards.
- *
- * It is possible to begin iteration from an arbitrary entry within
- * the list by passing @prev. @prev is updated on return to point to
- * the last entry passed to @func. To begin iterating from the
- * beginning of the list @prev must be %NULL.
- *
- * The restrictions for @func are the same as documented for
- * efivar_entry_iter().
- */
-int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
- void *data, struct efivar_entry **prev)
-{
- struct efivar_entry *entry, *n;
- int err = 0;
-
- if (!prev || !*prev) {
- list_for_each_entry_safe(entry, n, &__efivars->list, list) {
- err = func(entry, data);
- if (err)
- break;
- }
-
- if (prev)
- *prev = entry;
-
- return err;
- }
-
-
- list_for_each_entry_safe_continue((*prev), n, &__efivars->list, list) {
- err = func(*prev, data);
- if (err)
- break;
- }
-
- return err;
-}
-EXPORT_SYMBOL_GPL(__efivar_entry_iter);
-
-/**
- * efivar_entry_iter - iterate over variable list
- * @func: callback function
- * @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 *), void *data)
-{
- int err = 0;
-
- efivar_entry_iter_begin();
- err = __efivar_entry_iter(func, data, NULL);
- efivar_entry_iter_end();
-
- return err;
-}
-EXPORT_SYMBOL_GPL(efivar_entry_iter);
-
-/**
- * efivars_register - register an efivars
- * @efivars: efivars to register
- * @ops: efivars operations
- *
- * Only a single efivars can be registered at any time.
- */
-int efivars_register(struct efivars *efivars,
- const struct efivar_operations *ops)
-{
- if (__efivars)
- return -EBUSY;
-
- spin_lock_init(&efivars->lock);
- INIT_LIST_HEAD(&efivars->list);
- efivars->ops = ops;
-
- __efivars = efivars;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(efivars_register);
-
-/**
- * efivars_unregister - unregister an efivars
- * @efivars: efivars to unregister
- *
- * The caller must have already removed every entry from the list,
- * failure to do so is an error.
- */
-int efivars_unregister(struct efivars *efivars)
-{
- if (!__efivars) {
- printk(KERN_ERR "efivars not registered\n");
- return -EINVAL;
- }
-
- if (__efivars != efivars)
- return -EINVAL;
-
- if (!list_empty(&efivars->list)) {
- printk(KERN_ERR "efivars list not empty\n");
- return -EBUSY;
- }
-
- __efivars = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(efivars_unregister);
-
-static struct kobject *efivars_kobject;
-
-static int __init
-efivars_init(void)
-{
- int error;
-
- if (!efi_enabled(EFI_RUNTIME_SERVICES))
- return 0;
-
- /* Register the efi directory at /sys/firmware/efi */
- efi_kobj = kobject_create_and_add("efi", firmware_kobj);
- if (!efi_kobj) {
- printk(KERN_ERR "efivars: Firmware registration failed.\n");
- return -ENOMEM;
- }
-
- efivars_kobject = kobject_create_and_add("efivars", efi_kobj);
- if (!efivars_kobject) {
- pr_err("efivars: Subsystem registration failed.\n");
- kobject_put(efi_kobj);
- return -ENOMEM;
- }
-
- /* Don't forget the systab entry */
- error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
- if (error) {
- printk(KERN_ERR
- "efivars: Sysfs attribute export failed with error
%d.\n",
- error);
- }
-
- return error;
-}
-
-static void __exit
-efivars_exit(void)
-{
- if (efi_enabled(EFI_RUNTIME_SERVICES)) {
- kobject_put(efivars_kobject);
- kobject_put(efi_kobj);
- }
-}
-
-module_init(efivars_init);
-module_exit(efivars_exit);
-
diff --git a/include/linux/efi.h b/include/linux/efi.h
index b81928b..58b3cae 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -742,6 +742,8 @@ utf16_strlen(efi_char16_t *s)
return utf16_strnlen(s, ~0UL);
}
+extern struct kobject *efi_kobj;
+
#ifdef CONFIG_EFI_VARS
/*
* Return the number of bytes is the length of this string
--
1.8.1.2
--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html