ffb21c1
From cb6f23ee9601297c3c70d0cfe3d77dfde9bd119d Mon Sep 17 00:00:00 2001
ffb21c1
From: Matthew Garrett <mjg@redhat.com>
ffb21c1
Date: Fri, 5 Oct 2012 13:54:56 +0800
ffb21c1
Subject: [PATCH 01/17] efi: Add support for a UEFI variable filesystem
ffb21c1
ffb21c1
The existing EFI variables code only supports variables of up to 1024
ffb21c1
bytes. This limitation existed in version 0.99 of the EFI specification,
ffb21c1
but was removed before any full releases. Since variables can now be
ffb21c1
larger than a single page, sysfs isn't the best interface for this. So,
ffb21c1
instead, let's add a filesystem. Variables can be read, written and
ffb21c1
created, with the first 4 bytes of each variable representing its UEFI
ffb21c1
attributes. The create() method doesn't actually commit to flash since
ffb21c1
zero-length variables can't exist per-spec.
ffb21c1
ffb21c1
Updates from Jeremy Kerr <jeremy.kerr@canonical.com>.
ffb21c1
ffb21c1
Signed-off-by: Matthew Garrett <mjg@redhat.com>
ffb21c1
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 384 ++++++++++++++++++++++++++++++++++++++++++++-
ffb21c1
 include/linux/efi.h        |   5 +
ffb21c1
 2 files changed, 383 insertions(+), 6 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index d10c987..b605c48 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -80,6 +80,10 @@
ffb21c1
 #include <linux/slab.h>
ffb21c1
 #include <linux/pstore.h>
ffb21c1
 
ffb21c1
+#include <linux/fs.h>
ffb21c1
+#include <linux/ramfs.h>
ffb21c1
+#include <linux/pagemap.h>
ffb21c1
+
ffb21c1
 #include <asm/uaccess.h>
ffb21c1
 
ffb21c1
 #define EFIVARS_VERSION "0.08"
ffb21c1
@@ -91,6 +95,7 @@ MODULE_LICENSE("GPL");
ffb21c1
 MODULE_VERSION(EFIVARS_VERSION);
ffb21c1
 
ffb21c1
 #define DUMP_NAME_LEN 52
ffb21c1
+#define GUID_LEN 37
ffb21c1
 
ffb21c1
 /*
ffb21c1
  * The maximum size of VariableName + Data = 1024
ffb21c1
@@ -108,7 +113,6 @@ struct efi_variable {
ffb21c1
 	__u32         Attributes;
ffb21c1
 } __attribute__((packed));
ffb21c1
 
ffb21c1
-
ffb21c1
 struct efivar_entry {
ffb21c1
 	struct efivars *efivars;
ffb21c1
 	struct efi_variable var;
ffb21c1
@@ -122,6 +126,9 @@ struct efivar_attribute {
ffb21c1
 	ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
ffb21c1
 };
ffb21c1
 
ffb21c1
+static struct efivars __efivars;
ffb21c1
+static struct efivar_operations ops;
ffb21c1
+
ffb21c1
 #define PSTORE_EFI_ATTRIBUTES \
ffb21c1
 	(EFI_VARIABLE_NON_VOLATILE | \
ffb21c1
 	 EFI_VARIABLE_BOOTSERVICE_ACCESS | \
ffb21c1
@@ -629,14 +636,380 @@ static struct kobj_type efivar_ktype = {
ffb21c1
 	.default_attrs = def_attrs,
ffb21c1
 };
ffb21c1
 
ffb21c1
-static struct pstore_info efi_pstore_info;
ffb21c1
-
ffb21c1
 static inline void
ffb21c1
 efivar_unregister(struct efivar_entry *var)
ffb21c1
 {
ffb21c1
 	kobject_put(&var->kobj);
ffb21c1
 }
ffb21c1
 
ffb21c1
+static int efivarfs_file_open(struct inode *inode, struct file *file)
ffb21c1
+{
ffb21c1
+	file->private_data = inode->i_private;
ffb21c1
+	return 0;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
+		const char __user *userbuf, size_t count, loff_t *ppos)
ffb21c1
+{
ffb21c1
+	struct efivar_entry *var = file->private_data;
ffb21c1
+	struct efivars *efivars;
ffb21c1
+	efi_status_t status;
ffb21c1
+	void *data;
ffb21c1
+	u32 attributes;
ffb21c1
+	struct inode *inode = file->f_mapping->host;
ffb21c1
+	int datasize = count - sizeof(attributes);
ffb21c1
+
ffb21c1
+	if (count < sizeof(attributes))
ffb21c1
+		return -EINVAL;
ffb21c1
+
ffb21c1
+	data = kmalloc(datasize, GFP_KERNEL);
ffb21c1
+
ffb21c1
+	if (!data)
ffb21c1
+		return -ENOMEM;
ffb21c1
+
ffb21c1
+	efivars = var->efivars;
ffb21c1
+
ffb21c1
+	if (copy_from_user(&attributes, userbuf, sizeof(attributes))) {
ffb21c1
+		count = -EFAULT;
ffb21c1
+		goto out;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	if (attributes & ~(EFI_VARIABLE_MASK)) {
ffb21c1
+		count = -EINVAL;
ffb21c1
+		goto out;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) {
ffb21c1
+		count = -EFAULT;
ffb21c1
+		goto out;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	if (validate_var(&var->var, data, datasize) == false) {
ffb21c1
+		count = -EINVAL;
ffb21c1
+		goto out;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	status = efivars->ops->set_variable(var->var.VariableName,
ffb21c1
+					    &var->var.VendorGuid,
ffb21c1
+					    attributes, datasize,
ffb21c1
+					    data);
ffb21c1
+
ffb21c1
+	switch (status) {
ffb21c1
+	case EFI_SUCCESS:
ffb21c1
+		mutex_lock(&inode->i_mutex);
ffb21c1
+		i_size_write(inode, count);
ffb21c1
+		mutex_unlock(&inode->i_mutex);
ffb21c1
+		break;
ffb21c1
+	case EFI_INVALID_PARAMETER:
ffb21c1
+		count = -EINVAL;
ffb21c1
+		break;
ffb21c1
+	case EFI_OUT_OF_RESOURCES:
ffb21c1
+		count = -ENOSPC;
ffb21c1
+		break;
ffb21c1
+	case EFI_DEVICE_ERROR:
ffb21c1
+		count = -EIO;
ffb21c1
+		break;
ffb21c1
+	case EFI_WRITE_PROTECTED:
ffb21c1
+		count = -EROFS;
ffb21c1
+		break;
ffb21c1
+	case EFI_SECURITY_VIOLATION:
ffb21c1
+		count = -EACCES;
ffb21c1
+		break;
ffb21c1
+	case EFI_NOT_FOUND:
ffb21c1
+		count = -ENOENT;
ffb21c1
+		break;
ffb21c1
+	default:
ffb21c1
+		count = -EINVAL;
ffb21c1
+		break;
ffb21c1
+	}
ffb21c1
+out:
ffb21c1
+	kfree(data);
ffb21c1
+
ffb21c1
+	return count;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
+		size_t count, loff_t *ppos)
ffb21c1
+{
ffb21c1
+	struct efivar_entry *var = file->private_data;
ffb21c1
+	struct efivars *efivars = var->efivars;
ffb21c1
+	efi_status_t status;
ffb21c1
+	unsigned long datasize = 0;
ffb21c1
+	u32 attributes;
ffb21c1
+	void *data;
ffb21c1
+	ssize_t size;
ffb21c1
+
ffb21c1
+	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
+					    &var->var.VendorGuid,
ffb21c1
+					    &attributes, &datasize, NULL);
ffb21c1
+
ffb21c1
+	if (status != EFI_BUFFER_TOO_SMALL)
ffb21c1
+		return 0;
ffb21c1
+
ffb21c1
+	data = kmalloc(datasize + 4, GFP_KERNEL);
ffb21c1
+
ffb21c1
+	if (!data)
ffb21c1
+		return 0;
ffb21c1
+
ffb21c1
+	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
+					    &var->var.VendorGuid,
ffb21c1
+					    &attributes, &datasize,
ffb21c1
+					    (data + 4));
ffb21c1
+
ffb21c1
+	if (status != EFI_SUCCESS)
ffb21c1
+		return 0;
ffb21c1
+
ffb21c1
+	memcpy(data, &attributes, 4);
ffb21c1
+	size = simple_read_from_buffer(userbuf, count, ppos,
ffb21c1
+					data, datasize + 4);
ffb21c1
+	kfree(data);
ffb21c1
+
ffb21c1
+	return size;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static void efivarfs_evict_inode(struct inode *inode)
ffb21c1
+{
ffb21c1
+	clear_inode(inode);
ffb21c1
+}
ffb21c1
+
ffb21c1
+static const struct super_operations efivarfs_ops = {
ffb21c1
+	.statfs = simple_statfs,
ffb21c1
+	.drop_inode = generic_delete_inode,
ffb21c1
+	.evict_inode = efivarfs_evict_inode,
ffb21c1
+	.show_options = generic_show_options,
ffb21c1
+};
ffb21c1
+
ffb21c1
+static struct super_block *efivarfs_sb;
ffb21c1
+
ffb21c1
+static const struct inode_operations efivarfs_dir_inode_operations;
ffb21c1
+
ffb21c1
+static const struct file_operations efivarfs_file_operations = {
ffb21c1
+	.open	= efivarfs_file_open,
ffb21c1
+	.read	= efivarfs_file_read,
ffb21c1
+	.write	= efivarfs_file_write,
ffb21c1
+	.llseek	= no_llseek,
ffb21c1
+};
ffb21c1
+
ffb21c1
+static struct inode *efivarfs_get_inode(struct super_block *sb,
ffb21c1
+				const struct inode *dir, int mode, dev_t dev)
ffb21c1
+{
ffb21c1
+	struct inode *inode = new_inode(sb);
ffb21c1
+
ffb21c1
+	if (inode) {
ffb21c1
+		inode->i_ino = get_next_ino();
ffb21c1
+		inode->i_uid = inode->i_gid = 0;
ffb21c1
+		inode->i_mode = mode;
ffb21c1
+		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
ffb21c1
+		switch (mode & S_IFMT) {
ffb21c1
+		case S_IFREG:
ffb21c1
+			inode->i_fop = &efivarfs_file_operations;
ffb21c1
+			break;
ffb21c1
+		case S_IFDIR:
ffb21c1
+			inode->i_op = &efivarfs_dir_inode_operations;
ffb21c1
+			inode->i_fop = &simple_dir_operations;
ffb21c1
+			inc_nlink(inode);
ffb21c1
+			break;
ffb21c1
+		}
ffb21c1
+	}
ffb21c1
+	return inode;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
ffb21c1
+{
ffb21c1
+	guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]);
ffb21c1
+	guid->b[1] = hex_to_bin(str[4]) << 4 | hex_to_bin(str[5]);
ffb21c1
+	guid->b[2] = hex_to_bin(str[2]) << 4 | hex_to_bin(str[3]);
ffb21c1
+	guid->b[3] = hex_to_bin(str[0]) << 4 | hex_to_bin(str[1]);
ffb21c1
+	guid->b[4] = hex_to_bin(str[11]) << 4 | hex_to_bin(str[12]);
ffb21c1
+	guid->b[5] = hex_to_bin(str[9]) << 4 | hex_to_bin(str[10]);
ffb21c1
+	guid->b[6] = hex_to_bin(str[16]) << 4 | hex_to_bin(str[17]);
ffb21c1
+	guid->b[7] = hex_to_bin(str[14]) << 4 | hex_to_bin(str[15]);
ffb21c1
+	guid->b[8] = hex_to_bin(str[19]) << 4 | hex_to_bin(str[20]);
ffb21c1
+	guid->b[9] = hex_to_bin(str[21]) << 4 | hex_to_bin(str[22]);
ffb21c1
+	guid->b[10] = hex_to_bin(str[24]) << 4 | hex_to_bin(str[25]);
ffb21c1
+	guid->b[11] = hex_to_bin(str[26]) << 4 | hex_to_bin(str[27]);
ffb21c1
+	guid->b[12] = hex_to_bin(str[28]) << 4 | hex_to_bin(str[29]);
ffb21c1
+	guid->b[13] = hex_to_bin(str[30]) << 4 | hex_to_bin(str[31]);
ffb21c1
+	guid->b[14] = hex_to_bin(str[32]) << 4 | hex_to_bin(str[33]);
ffb21c1
+	guid->b[15] = hex_to_bin(str[34]) << 4 | hex_to_bin(str[35]);
ffb21c1
+}
ffb21c1
+
ffb21c1
+static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
+			  umode_t mode, bool excl)
ffb21c1
+{
ffb21c1
+	struct inode *inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
ffb21c1
+	struct efivars *efivars = &__efivars;
ffb21c1
+	struct efivar_entry *var;
ffb21c1
+	int namelen, i = 0, err = 0;
ffb21c1
+
ffb21c1
+	if (dentry->d_name.len < 38)
ffb21c1
+		return -EINVAL;
ffb21c1
+
ffb21c1
+	if (!inode)
ffb21c1
+		return -ENOSPC;
ffb21c1
+
ffb21c1
+	var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
ffb21c1
+
ffb21c1
+	if (!var)
ffb21c1
+		return -ENOMEM;
ffb21c1
+
ffb21c1
+	namelen = dentry->d_name.len - GUID_LEN;
ffb21c1
+
ffb21c1
+	efivarfs_hex_to_guid(dentry->d_name.name + namelen + 1,
ffb21c1
+			&var->var.VendorGuid);
ffb21c1
+
ffb21c1
+	for (i = 0; i < namelen; i++)
ffb21c1
+		var->var.VariableName[i] = dentry->d_name.name[i];
ffb21c1
+
ffb21c1
+	var->var.VariableName[i] = '\0';
ffb21c1
+
ffb21c1
+	inode->i_private = var;
ffb21c1
+	var->efivars = efivars;
ffb21c1
+	var->kobj.kset = efivars->kset;
ffb21c1
+
ffb21c1
+	err = kobject_init_and_add(&var->kobj, &efivar_ktype, NULL, "%s",
ffb21c1
+			     dentry->d_name.name);
ffb21c1
+	if (err)
ffb21c1
+		goto out;
ffb21c1
+
ffb21c1
+	kobject_uevent(&var->kobj, KOBJ_ADD);
ffb21c1
+	spin_lock(&efivars->lock);
ffb21c1
+	list_add(&var->list, &efivars->list);
ffb21c1
+	spin_unlock(&efivars->lock);
ffb21c1
+	d_instantiate(dentry, inode);
ffb21c1
+	dget(dentry);
ffb21c1
+out:
ffb21c1
+	if (err)
ffb21c1
+		kfree(var);
ffb21c1
+	return err;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
ffb21c1
+{
ffb21c1
+	struct efivar_entry *var = dentry->d_inode->i_private;
ffb21c1
+	struct efivars *efivars = var->efivars;
ffb21c1
+	efi_status_t status;
ffb21c1
+
ffb21c1
+	spin_lock(&efivars->lock);
ffb21c1
+
ffb21c1
+	status = efivars->ops->set_variable(var->var.VariableName,
ffb21c1
+					    &var->var.VendorGuid,
ffb21c1
+					    0, 0, NULL);
ffb21c1
+
ffb21c1
+	if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) {
ffb21c1
+		list_del(&var->list);
ffb21c1
+		spin_unlock(&efivars->lock);
ffb21c1
+		efivar_unregister(var);
ffb21c1
+		drop_nlink(dir);
ffb21c1
+		dput(dentry);
ffb21c1
+		return 0;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	spin_unlock(&efivars->lock);
ffb21c1
+	return -EINVAL;
ffb21c1
+};
ffb21c1
+
ffb21c1
+int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
+{
ffb21c1
+	struct inode *inode = NULL;
ffb21c1
+	struct dentry *root;
ffb21c1
+	struct efivar_entry *entry, *n;
ffb21c1
+	struct efivars *efivars = &__efivars;
ffb21c1
+	int err;
ffb21c1
+
ffb21c1
+	efivarfs_sb = sb;
ffb21c1
+
ffb21c1
+	sb->s_maxbytes          = MAX_LFS_FILESIZE;
ffb21c1
+	sb->s_blocksize         = PAGE_CACHE_SIZE;
ffb21c1
+	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
ffb21c1
+	sb->s_magic             = PSTOREFS_MAGIC;
ffb21c1
+	sb->s_op                = &efivarfs_ops;
ffb21c1
+	sb->s_time_gran         = 1;
ffb21c1
+
ffb21c1
+	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
ffb21c1
+	if (!inode) {
ffb21c1
+		err = -ENOMEM;
ffb21c1
+		goto fail;
ffb21c1
+	}
ffb21c1
+	inode->i_op = &efivarfs_dir_inode_operations;
ffb21c1
+
ffb21c1
+	root = d_make_root(inode);
ffb21c1
+	sb->s_root = root;
ffb21c1
+	if (!root) {
ffb21c1
+		err = -ENOMEM;
ffb21c1
+		goto fail;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	list_for_each_entry_safe(entry, n, &efivars->list, list) {
ffb21c1
+		struct inode *inode;
ffb21c1
+		struct dentry *dentry, *root = efivarfs_sb->s_root;
ffb21c1
+		char *name;
ffb21c1
+		unsigned long size = 0;
ffb21c1
+		int len, i;
ffb21c1
+
ffb21c1
+		len = utf16_strlen(entry->var.VariableName);
ffb21c1
+
ffb21c1
+		/* GUID plus trailing NULL */
ffb21c1
+		name = kmalloc(len + 38, GFP_ATOMIC);
ffb21c1
+
ffb21c1
+		for (i = 0; i < len; i++)
ffb21c1
+			name[i] = entry->var.VariableName[i] & 0xFF;
ffb21c1
+
ffb21c1
+		name[len] = '-';
ffb21c1
+
ffb21c1
+		efi_guid_unparse(&entry->var.VendorGuid, name + len + 1);
ffb21c1
+
ffb21c1
+		name[len+GUID_LEN] = '\0';
ffb21c1
+
ffb21c1
+		inode = efivarfs_get_inode(efivarfs_sb, root->d_inode,
ffb21c1
+					  S_IFREG | 0644, 0);
ffb21c1
+		dentry = d_alloc_name(root, name);
ffb21c1
+
ffb21c1
+		efivars->ops->get_variable(entry->var.VariableName,
ffb21c1
+					   &entry->var.VendorGuid,
ffb21c1
+					   &entry->var.Attributes,
ffb21c1
+					   &size,
ffb21c1
+					   NULL);
ffb21c1
+
ffb21c1
+		mutex_lock(&inode->i_mutex);
ffb21c1
+		inode->i_private = entry;
ffb21c1
+		i_size_write(inode, size+4);
ffb21c1
+		mutex_unlock(&inode->i_mutex);
ffb21c1
+		d_add(dentry, inode);
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	return 0;
ffb21c1
+fail:
ffb21c1
+	iput(inode);
ffb21c1
+	return err;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
ffb21c1
+				    int flags, const char *dev_name, void *data)
ffb21c1
+{
ffb21c1
+	return mount_single(fs_type, flags, data, efivarfs_fill_super);
ffb21c1
+}
ffb21c1
+
ffb21c1
+static void efivarfs_kill_sb(struct super_block *sb)
ffb21c1
+{
ffb21c1
+	kill_litter_super(sb);
ffb21c1
+	efivarfs_sb = NULL;
ffb21c1
+}
ffb21c1
+
ffb21c1
+static struct file_system_type efivarfs_type = {
ffb21c1
+	.name    = "efivarfs",
ffb21c1
+	.mount   = efivarfs_mount,
ffb21c1
+	.kill_sb = efivarfs_kill_sb,
ffb21c1
+};
ffb21c1
+
ffb21c1
+static const struct inode_operations efivarfs_dir_inode_operations = {
ffb21c1
+	.lookup = simple_lookup,
ffb21c1
+	.unlink = efivarfs_unlink,
ffb21c1
+	.create = efivarfs_create,
ffb21c1
+};
ffb21c1
+
ffb21c1
+static struct pstore_info efi_pstore_info;
ffb21c1
+
ffb21c1
 #ifdef CONFIG_PSTORE
ffb21c1
 
ffb21c1
 static int efi_pstore_open(struct pstore_info *psi)
ffb21c1
@@ -1198,6 +1571,8 @@ int register_efivars(struct efivars *efivars,
ffb21c1
 		pstore_register(&efivars->efi_pstore_info);
ffb21c1
 	}
ffb21c1
 
ffb21c1
+	register_filesystem(&efivarfs_type);
ffb21c1
+
ffb21c1
 out:
ffb21c1
 	kfree(variable_name);
ffb21c1
 
ffb21c1
@@ -1205,9 +1580,6 @@ out:
ffb21c1
 }
ffb21c1
 EXPORT_SYMBOL_GPL(register_efivars);
ffb21c1
 
ffb21c1
-static struct efivars __efivars;
ffb21c1
-static struct efivar_operations ops;
ffb21c1
-
ffb21c1
 /*
ffb21c1
  * For now we register the efi subsystem with the firmware subsystem
ffb21c1
  * and the vars subsystem with the efi subsystem.  In the future, it
ffb21c1
diff --git a/include/linux/efi.h b/include/linux/efi.h
ffb21c1
index 8670eb1..b2af157 100644
ffb21c1
--- a/include/linux/efi.h
ffb21c1
+++ b/include/linux/efi.h
ffb21c1
@@ -29,7 +29,12 @@
ffb21c1
 #define EFI_UNSUPPORTED		( 3 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
 #define EFI_BAD_BUFFER_SIZE     ( 4 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
 #define EFI_BUFFER_TOO_SMALL	( 5 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
+#define EFI_NOT_READY		( 6 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
+#define EFI_DEVICE_ERROR	( 7 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
+#define EFI_WRITE_PROTECTED	( 8 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
+#define EFI_OUT_OF_RESOURCES	( 9 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
 #define EFI_NOT_FOUND		(14 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
+#define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
ffb21c1
 
ffb21c1
 typedef unsigned long efi_status_t;
ffb21c1
 typedef u8 efi_bool_t;
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 2fc1dc88e97665c70f203f0132232ea55e8dd7eb Mon Sep 17 00:00:00 2001
ffb21c1
From: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Date: Fri, 5 Oct 2012 13:54:56 +0800
ffb21c1
Subject: [PATCH 02/17] efi: Handle deletions and size changes in
ffb21c1
 efivarfs_write_file
ffb21c1
ffb21c1
A write to an efivarfs file will not always result in a variable of
ffb21c1
'count' size after the EFI SetVariable() call. We may have appended to
ffb21c1
the existing data (ie, with the EFI_VARIABLE_APPEND_WRITE attribute), or
ffb21c1
even have deleted the variable (with an authenticated variable update,
ffb21c1
with a zero datasize).
ffb21c1
ffb21c1
This change re-reads the updated variable from firmware, to check for
ffb21c1
size changes and deletions. In the latter case, we need to drop the
ffb21c1
dentry.
ffb21c1
ffb21c1
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 49 ++++++++++++++++++++++++++++++++++++----------
ffb21c1
 1 file changed, 39 insertions(+), 10 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index b605c48..d7658b4 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -658,6 +658,7 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 	u32 attributes;
ffb21c1
 	struct inode *inode = file->f_mapping->host;
ffb21c1
 	int datasize = count - sizeof(attributes);
ffb21c1
+	unsigned long newdatasize;
ffb21c1
 
ffb21c1
 	if (count < sizeof(attributes))
ffb21c1
 		return -EINVAL;
ffb21c1
@@ -696,32 +697,60 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 
ffb21c1
 	switch (status) {
ffb21c1
 	case EFI_SUCCESS:
ffb21c1
-		mutex_lock(&inode->i_mutex);
ffb21c1
-		i_size_write(inode, count);
ffb21c1
-		mutex_unlock(&inode->i_mutex);
ffb21c1
 		break;
ffb21c1
 	case EFI_INVALID_PARAMETER:
ffb21c1
 		count = -EINVAL;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	case EFI_OUT_OF_RESOURCES:
ffb21c1
 		count = -ENOSPC;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	case EFI_DEVICE_ERROR:
ffb21c1
 		count = -EIO;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	case EFI_WRITE_PROTECTED:
ffb21c1
 		count = -EROFS;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	case EFI_SECURITY_VIOLATION:
ffb21c1
 		count = -EACCES;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	case EFI_NOT_FOUND:
ffb21c1
 		count = -ENOENT;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	default:
ffb21c1
 		count = -EINVAL;
ffb21c1
-		break;
ffb21c1
+		goto out;
ffb21c1
 	}
ffb21c1
+
ffb21c1
+	/*
ffb21c1
+	 * Writing to the variable may have caused a change in size (which
ffb21c1
+	 * could either be an append or an overwrite), or the variable to be
ffb21c1
+	 * deleted. Perform a GetVariable() so we can tell what actually
ffb21c1
+	 * happened.
ffb21c1
+	 */
ffb21c1
+	newdatasize = 0;
ffb21c1
+	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
+					    &var->var.VendorGuid,
ffb21c1
+					    NULL, &newdatasize,
ffb21c1
+					    NULL);
ffb21c1
+
ffb21c1
+	if (status == EFI_BUFFER_TOO_SMALL) {
ffb21c1
+		mutex_lock(&inode->i_mutex);
ffb21c1
+		i_size_write(inode, newdatasize + sizeof(attributes));
ffb21c1
+		mutex_unlock(&inode->i_mutex);
ffb21c1
+
ffb21c1
+	} else if (status == EFI_NOT_FOUND) {
ffb21c1
+		spin_lock(&efivars->lock);
ffb21c1
+		list_del(&var->list);
ffb21c1
+		spin_unlock(&efivars->lock);
ffb21c1
+		efivar_unregister(var);
ffb21c1
+		drop_nlink(inode);
ffb21c1
+		dput(file->f_dentry);
ffb21c1
+
ffb21c1
+	} else {
ffb21c1
+		pr_warn("efivarfs: inconsistent EFI variable implementation? "
ffb21c1
+				"status = %lx\n", status);
ffb21c1
+	}
ffb21c1
+
ffb21c1
 out:
ffb21c1
 	kfree(data);
ffb21c1
 
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From c98611fc95672862950c9bc4d6a3a4c4453a3c3e Mon Sep 17 00:00:00 2001
ffb21c1
From: "Lee, Chun-Yi" <joeyli.kernel@gmail.com>
ffb21c1
Date: Fri, 5 Oct 2012 13:54:56 +0800
ffb21c1
Subject: [PATCH 03/17] efi: add efivars kobject to efi sysfs folder
ffb21c1
ffb21c1
UEFI variable filesystem need a new mount point, so this patch add
ffb21c1
efivars kobject to efi_kobj for create a /sys/firmware/efi/efivars
ffb21c1
folder.
ffb21c1
ffb21c1
Cc: Matthew Garrett <mjg@redhat.com>
ffb21c1
Cc: H. Peter Anvin <hpa@zytor.com>
ffb21c1
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
ffb21c1
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 9 +++++++++
ffb21c1
 include/linux/efi.h        | 1 +
ffb21c1
 2 files changed, 10 insertions(+)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index d7658b4..6793965 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -1527,6 +1527,7 @@ void unregister_efivars(struct efivars *efivars)
ffb21c1
 		sysfs_remove_bin_file(&efivars->kset->kobj, efivars->del_var);
ffb21c1
 	kfree(efivars->new_var);
ffb21c1
 	kfree(efivars->del_var);
ffb21c1
+	kobject_put(efivars->kobject);
ffb21c1
 	kset_unregister(efivars->kset);
ffb21c1
 }
ffb21c1
 EXPORT_SYMBOL_GPL(unregister_efivars);
ffb21c1
@@ -1558,6 +1559,14 @@ int register_efivars(struct efivars *efivars,
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
+	efivars->kobject = kobject_create_and_add("efivars", parent_kobj);
ffb21c1
+	if (!efivars->kobject) {
ffb21c1
+		pr_err("efivars: Subsystem registration failed.\n");
ffb21c1
+		error = -ENOMEM;
ffb21c1
+		kset_unregister(efivars->kset);
ffb21c1
+		goto out;
ffb21c1
+	}
ffb21c1
+
ffb21c1
 	/*
ffb21c1
 	 * Per EFI spec, the maximum storage allocated for both
ffb21c1
 	 * the variable name and variable data is 1024 bytes.
ffb21c1
diff --git a/include/linux/efi.h b/include/linux/efi.h
ffb21c1
index b2af157..337aefb 100644
ffb21c1
--- a/include/linux/efi.h
ffb21c1
+++ b/include/linux/efi.h
ffb21c1
@@ -662,6 +662,7 @@ struct efivars {
ffb21c1
 	spinlock_t lock;
ffb21c1
 	struct list_head list;
ffb21c1
 	struct kset *kset;
ffb21c1
+	struct kobject *kobject;
ffb21c1
 	struct bin_attribute *new_var, *del_var;
ffb21c1
 	const struct efivar_operations *ops;
ffb21c1
 	struct efivar_entry *walk_entry;
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 8ef5f49da57087022f2031820ceb3b1c6101d76c Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Thu, 4 Oct 2012 09:57:31 +0100
ffb21c1
Subject: [PATCH 04/17] efivarfs: Add documentation for the EFI variable
ffb21c1
 filesystem
ffb21c1
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 Documentation/filesystems/00-INDEX     |  2 ++
ffb21c1
 Documentation/filesystems/efivarfs.txt | 16 ++++++++++++++++
ffb21c1
 2 files changed, 18 insertions(+)
ffb21c1
 create mode 100644 Documentation/filesystems/efivarfs.txt
ffb21c1
ffb21c1
diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX
ffb21c1
index 8c624a1..7b52ba7 100644
ffb21c1
--- a/Documentation/filesystems/00-INDEX
ffb21c1
+++ b/Documentation/filesystems/00-INDEX
ffb21c1
@@ -38,6 +38,8 @@ dnotify_test.c
ffb21c1
 	- example program for dnotify
ffb21c1
 ecryptfs.txt
ffb21c1
 	- docs on eCryptfs: stacked cryptographic filesystem for Linux.
ffb21c1
+efivarfs.txt
ffb21c1
+	- info for the efivarfs filesystem.
ffb21c1
 exofs.txt
ffb21c1
 	- info, usage, mount options, design about EXOFS.
ffb21c1
 ext2.txt
ffb21c1
diff --git a/Documentation/filesystems/efivarfs.txt b/Documentation/filesystems/efivarfs.txt
ffb21c1
new file mode 100644
ffb21c1
index 0000000..c477af0
ffb21c1
--- /dev/null
ffb21c1
+++ b/Documentation/filesystems/efivarfs.txt
ffb21c1
@@ -0,0 +1,16 @@
ffb21c1
+
ffb21c1
+efivarfs - a (U)EFI variable filesystem
ffb21c1
+
ffb21c1
+The efivarfs filesystem was created to address the shortcomings of
ffb21c1
+using entries in sysfs to maintain EFI variables. The old sysfs EFI
ffb21c1
+variables code only supported variables of up to 1024 bytes. This
ffb21c1
+limitation existed in version 0.99 of the EFI specification, but was
ffb21c1
+removed before any full releases. Since variables can now be larger
ffb21c1
+than a single page, sysfs isn't the best interface for this.
ffb21c1
+
ffb21c1
+Variables can be created, deleted and modified with the efivarfs
ffb21c1
+filesystem.
ffb21c1
+
ffb21c1
+efivarfs is typically mounted like this,
ffb21c1
+
ffb21c1
+	mount -t efivarfs none /sys/firmware/efi/efivars
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From f69c39248e2f1eebf24f5ee55139602c56d15aec Mon Sep 17 00:00:00 2001
ffb21c1
From: Andy Whitcroft <apw@canonical.com>
ffb21c1
Date: Thu, 11 Oct 2012 11:32:17 +0100
ffb21c1
Subject: [PATCH 05/17] efivarfs: efivarfs_file_read ensure we free data in
ffb21c1
 error paths
ffb21c1
ffb21c1
Signed-off-by: Andy Whitcroft <apw@canonical.com>
ffb21c1
Acked-by: Matthew Garrett <mjg@redhat.com>
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 6 +++---
ffb21c1
 1 file changed, 3 insertions(+), 3 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 6793965..b7c9a32 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -766,7 +766,7 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 	unsigned long datasize = 0;
ffb21c1
 	u32 attributes;
ffb21c1
 	void *data;
ffb21c1
-	ssize_t size;
ffb21c1
+	ssize_t size = 0;
ffb21c1
 
ffb21c1
 	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
 					    &var->var.VendorGuid,
ffb21c1
@@ -784,13 +784,13 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 					    &var->var.VendorGuid,
ffb21c1
 					    &attributes, &datasize,
ffb21c1
 					    (data + 4));
ffb21c1
-
ffb21c1
 	if (status != EFI_SUCCESS)
ffb21c1
-		return 0;
ffb21c1
+		goto out_free;
ffb21c1
 
ffb21c1
 	memcpy(data, &attributes, 4);
ffb21c1
 	size = simple_read_from_buffer(userbuf, count, ppos,
ffb21c1
 					data, datasize + 4);
ffb21c1
+out_free:
ffb21c1
 	kfree(data);
ffb21c1
 
ffb21c1
 	return size;
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 429136e16c7c43bbebb8b8030203161a2d3fc3ce Mon Sep 17 00:00:00 2001
ffb21c1
From: Andy Whitcroft <apw@canonical.com>
ffb21c1
Date: Thu, 11 Oct 2012 11:32:18 +0100
ffb21c1
Subject: [PATCH 06/17] efivarfs: efivarfs_create() ensure we drop our
ffb21c1
 reference on inode on error
ffb21c1
ffb21c1
Signed-off-by: Andy Whitcroft <apw@canonical.com>
ffb21c1
Acked-by: Matthew Garrett <mjg@redhat.com>
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 14 +++++++++-----
ffb21c1
 1 file changed, 9 insertions(+), 5 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index b7c9a32..6e5f367 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -866,7 +866,7 @@ static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
ffb21c1
 static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
 			  umode_t mode, bool excl)
ffb21c1
 {
ffb21c1
-	struct inode *inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
ffb21c1
+	struct inode *inode;
ffb21c1
 	struct efivars *efivars = &__efivars;
ffb21c1
 	struct efivar_entry *var;
ffb21c1
 	int namelen, i = 0, err = 0;
ffb21c1
@@ -874,13 +874,15 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
 	if (dentry->d_name.len < 38)
ffb21c1
 		return -EINVAL;
ffb21c1
 
ffb21c1
+	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
ffb21c1
 	if (!inode)
ffb21c1
 		return -ENOSPC;
ffb21c1
 
ffb21c1
 	var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
ffb21c1
-
ffb21c1
-	if (!var)
ffb21c1
-		return -ENOMEM;
ffb21c1
+	if (!var) {
ffb21c1
+		err = -ENOMEM;
ffb21c1
+		goto out;
ffb21c1
+	}
ffb21c1
 
ffb21c1
 	namelen = dentry->d_name.len - GUID_LEN;
ffb21c1
 
ffb21c1
@@ -908,8 +910,10 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
 	d_instantiate(dentry, inode);
ffb21c1
 	dget(dentry);
ffb21c1
 out:
ffb21c1
-	if (err)
ffb21c1
+	if (err) {
ffb21c1
 		kfree(var);
ffb21c1
+		iput(inode);
ffb21c1
+	}
ffb21c1
 	return err;
ffb21c1
 }
ffb21c1
 
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 1a19268e8a4bae43c1feb3f71ee468c6bc70aca6 Mon Sep 17 00:00:00 2001
ffb21c1
From: Andy Whitcroft <apw@canonical.com>
ffb21c1
Date: Thu, 11 Oct 2012 11:32:19 +0100
ffb21c1
Subject: [PATCH 07/17] efivarfs: efivarfs_fill_super() fix inode reference
ffb21c1
 counts
ffb21c1
ffb21c1
When d_make_root() fails it will automatically drop the reference
ffb21c1
on the root inode.  We should not be doing so as well.
ffb21c1
ffb21c1
Signed-off-by: Andy Whitcroft <apw@canonical.com>
ffb21c1
Acked-by: Matthew Garrett <mjg@redhat.com>
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 16 ++++------------
ffb21c1
 1 file changed, 4 insertions(+), 12 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 6e5f367..adfc486 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -948,7 +948,6 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 	struct dentry *root;
ffb21c1
 	struct efivar_entry *entry, *n;
ffb21c1
 	struct efivars *efivars = &__efivars;
ffb21c1
-	int err;
ffb21c1
 
ffb21c1
 	efivarfs_sb = sb;
ffb21c1
 
ffb21c1
@@ -960,18 +959,14 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 	sb->s_time_gran         = 1;
ffb21c1
 
ffb21c1
 	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
ffb21c1
-	if (!inode) {
ffb21c1
-		err = -ENOMEM;
ffb21c1
-		goto fail;
ffb21c1
-	}
ffb21c1
+	if (!inode)
ffb21c1
+		return -ENOMEM;
ffb21c1
 	inode->i_op = &efivarfs_dir_inode_operations;
ffb21c1
 
ffb21c1
 	root = d_make_root(inode);
ffb21c1
 	sb->s_root = root;
ffb21c1
-	if (!root) {
ffb21c1
-		err = -ENOMEM;
ffb21c1
-		goto fail;
ffb21c1
-	}
ffb21c1
+	if (!root)
ffb21c1
+		return -ENOMEM;
ffb21c1
 
ffb21c1
 	list_for_each_entry_safe(entry, n, &efivars->list, list) {
ffb21c1
 		struct inode *inode;
ffb21c1
@@ -1012,9 +1007,6 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	return 0;
ffb21c1
-fail:
ffb21c1
-	iput(inode);
ffb21c1
-	return err;
ffb21c1
 }
ffb21c1
 
ffb21c1
 static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From f0d90a4024493aed6fc77ce5cd3b93f278fed9c0 Mon Sep 17 00:00:00 2001
ffb21c1
From: Andy Whitcroft <apw@canonical.com>
ffb21c1
Date: Thu, 11 Oct 2012 11:32:20 +0100
ffb21c1
Subject: [PATCH 08/17] efivarfs: efivarfs_fill_super() ensure we free our
ffb21c1
 temporary name
ffb21c1
ffb21c1
d_alloc_name() copies the passed name to new storage, once complete we
ffb21c1
no longer need our name.
ffb21c1
ffb21c1
Signed-off-by: Andy Whitcroft <apw@canonical.com>
ffb21c1
Acked-by: Matthew Garrett <mjg@redhat.com>
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 2 ++
ffb21c1
 1 file changed, 2 insertions(+)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index adfc486..36b3dd6 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -992,6 +992,8 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 		inode = efivarfs_get_inode(efivarfs_sb, root->d_inode,
ffb21c1
 					  S_IFREG | 0644, 0);
ffb21c1
 		dentry = d_alloc_name(root, name);
ffb21c1
+		/* copied by the above to local storage in the dentry. */
ffb21c1
+		kfree(name);
ffb21c1
 
ffb21c1
 		efivars->ops->get_variable(entry->var.VariableName,
ffb21c1
 					   &entry->var.VendorGuid,
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From c4cf244c318218153200d0011d8ef0ebcf3146ea Mon Sep 17 00:00:00 2001
ffb21c1
From: Andy Whitcroft <apw@canonical.com>
ffb21c1
Date: Thu, 11 Oct 2012 11:32:21 +0100
ffb21c1
Subject: [PATCH 09/17] efivarfs: efivarfs_fill_super() ensure we clean up
ffb21c1
 correctly on error
ffb21c1
ffb21c1
Ensure we free both the name and inode on error when building the
ffb21c1
individual variables.
ffb21c1
ffb21c1
Signed-off-by: Andy Whitcroft <apw@canonical.com>
ffb21c1
Acked-by: Matthew Garrett <mjg@redhat.com>
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 20 ++++++++++++++++++--
ffb21c1
 1 file changed, 18 insertions(+), 2 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 36b3dd6..216086d 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -948,6 +948,7 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 	struct dentry *root;
ffb21c1
 	struct efivar_entry *entry, *n;
ffb21c1
 	struct efivars *efivars = &__efivars;
ffb21c1
+	char *name;
ffb21c1
 
ffb21c1
 	efivarfs_sb = sb;
ffb21c1
 
ffb21c1
@@ -969,16 +970,18 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 		return -ENOMEM;
ffb21c1
 
ffb21c1
 	list_for_each_entry_safe(entry, n, &efivars->list, list) {
ffb21c1
-		struct inode *inode;
ffb21c1
 		struct dentry *dentry, *root = efivarfs_sb->s_root;
ffb21c1
-		char *name;
ffb21c1
 		unsigned long size = 0;
ffb21c1
 		int len, i;
ffb21c1
 
ffb21c1
+		inode = NULL;
ffb21c1
+
ffb21c1
 		len = utf16_strlen(entry->var.VariableName);
ffb21c1
 
ffb21c1
 		/* GUID plus trailing NULL */
ffb21c1
 		name = kmalloc(len + 38, GFP_ATOMIC);
ffb21c1
+		if (!name)
ffb21c1
+			goto fail;
ffb21c1
 
ffb21c1
 		for (i = 0; i < len; i++)
ffb21c1
 			name[i] = entry->var.VariableName[i] & 0xFF;
ffb21c1
@@ -991,7 +994,13 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 
ffb21c1
 		inode = efivarfs_get_inode(efivarfs_sb, root->d_inode,
ffb21c1
 					  S_IFREG | 0644, 0);
ffb21c1
+		if (!inode)
ffb21c1
+			goto fail_name;
ffb21c1
+
ffb21c1
 		dentry = d_alloc_name(root, name);
ffb21c1
+		if (!dentry)
ffb21c1
+			goto fail_inode;
ffb21c1
+
ffb21c1
 		/* copied by the above to local storage in the dentry. */
ffb21c1
 		kfree(name);
ffb21c1
 
ffb21c1
@@ -1009,6 +1018,13 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	return 0;
ffb21c1
+
ffb21c1
+fail_inode:
ffb21c1
+	iput(inode);
ffb21c1
+fail_name:
ffb21c1
+	kfree(name);
ffb21c1
+fail:
ffb21c1
+	return -ENOMEM;
ffb21c1
 }
ffb21c1
 
ffb21c1
 static struct dentry *efivarfs_mount(struct file_system_type *fs_type,
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From d3b7165568bcb50e4526c3dadda59e43f6681bc0 Mon Sep 17 00:00:00 2001
ffb21c1
From: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Date: Thu, 11 Oct 2012 21:19:11 +0800
ffb21c1
Subject: [PATCH 10/17] efivarfs: Implement exclusive access for
ffb21c1
 {get,set}_variable
ffb21c1
ffb21c1
Currently, efivarfs does not enforce exclusion over the get_variable and
ffb21c1
set_variable operations. Section 7.1 of UEFI requires us to only allow a
ffb21c1
single processor to enter {get,set}_variable services at once.
ffb21c1
ffb21c1
This change acquires the efivars->lock over calls to these operations
ffb21c1
from the efivarfs paths.
ffb21c1
ffb21c1
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 68 +++++++++++++++++++++++++++++-----------------
ffb21c1
 1 file changed, 43 insertions(+), 25 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 216086d..d478c56 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -690,35 +690,45 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
+	/*
ffb21c1
+	 * The lock here protects the get_variable call, the conditional
ffb21c1
+	 * set_variable call, and removal of the variable from the efivars
ffb21c1
+	 * list (in the case of an authenticated delete).
ffb21c1
+	 */
ffb21c1
+	spin_lock(&efivars->lock);
ffb21c1
+
ffb21c1
 	status = efivars->ops->set_variable(var->var.VariableName,
ffb21c1
 					    &var->var.VendorGuid,
ffb21c1
 					    attributes, datasize,
ffb21c1
 					    data);
ffb21c1
 
ffb21c1
-	switch (status) {
ffb21c1
-	case EFI_SUCCESS:
ffb21c1
-		break;
ffb21c1
-	case EFI_INVALID_PARAMETER:
ffb21c1
-		count = -EINVAL;
ffb21c1
-		goto out;
ffb21c1
-	case EFI_OUT_OF_RESOURCES:
ffb21c1
-		count = -ENOSPC;
ffb21c1
-		goto out;
ffb21c1
-	case EFI_DEVICE_ERROR:
ffb21c1
-		count = -EIO;
ffb21c1
-		goto out;
ffb21c1
-	case EFI_WRITE_PROTECTED:
ffb21c1
-		count = -EROFS;
ffb21c1
-		goto out;
ffb21c1
-	case EFI_SECURITY_VIOLATION:
ffb21c1
-		count = -EACCES;
ffb21c1
-		goto out;
ffb21c1
-	case EFI_NOT_FOUND:
ffb21c1
-		count = -ENOENT;
ffb21c1
-		goto out;
ffb21c1
-	default:
ffb21c1
-		count = -EINVAL;
ffb21c1
-		goto out;
ffb21c1
+	if (status != EFI_SUCCESS) {
ffb21c1
+		spin_unlock(&efivars->lock);
ffb21c1
+		kfree(data);
ffb21c1
+
ffb21c1
+		switch (status) {
ffb21c1
+		case EFI_INVALID_PARAMETER:
ffb21c1
+			count = -EINVAL;
ffb21c1
+			break;
ffb21c1
+		case EFI_OUT_OF_RESOURCES:
ffb21c1
+			count = -ENOSPC;
ffb21c1
+			break;
ffb21c1
+		case EFI_DEVICE_ERROR:
ffb21c1
+			count = -EIO;
ffb21c1
+			break;
ffb21c1
+		case EFI_WRITE_PROTECTED:
ffb21c1
+			count = -EROFS;
ffb21c1
+			break;
ffb21c1
+		case EFI_SECURITY_VIOLATION:
ffb21c1
+			count = -EACCES;
ffb21c1
+			break;
ffb21c1
+		case EFI_NOT_FOUND:
ffb21c1
+			count = -ENOENT;
ffb21c1
+			break;
ffb21c1
+		default:
ffb21c1
+			count = -EINVAL;
ffb21c1
+		}
ffb21c1
+		return count;
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	/*
ffb21c1
@@ -734,12 +744,12 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 					    NULL);
ffb21c1
 
ffb21c1
 	if (status == EFI_BUFFER_TOO_SMALL) {
ffb21c1
+		spin_unlock(&efivars->lock);
ffb21c1
 		mutex_lock(&inode->i_mutex);
ffb21c1
 		i_size_write(inode, newdatasize + sizeof(attributes));
ffb21c1
 		mutex_unlock(&inode->i_mutex);
ffb21c1
 
ffb21c1
 	} else if (status == EFI_NOT_FOUND) {
ffb21c1
-		spin_lock(&efivars->lock);
ffb21c1
 		list_del(&var->list);
ffb21c1
 		spin_unlock(&efivars->lock);
ffb21c1
 		efivar_unregister(var);
ffb21c1
@@ -747,6 +757,7 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 		dput(file->f_dentry);
ffb21c1
 
ffb21c1
 	} else {
ffb21c1
+		spin_unlock(&efivars->lock);
ffb21c1
 		pr_warn("efivarfs: inconsistent EFI variable implementation? "
ffb21c1
 				"status = %lx\n", status);
ffb21c1
 	}
ffb21c1
@@ -768,9 +779,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 	void *data;
ffb21c1
 	ssize_t size = 0;
ffb21c1
 
ffb21c1
+	spin_lock(&efivars->lock);
ffb21c1
 	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
 					    &var->var.VendorGuid,
ffb21c1
 					    &attributes, &datasize, NULL);
ffb21c1
+	spin_unlock(&efivars->lock);
ffb21c1
 
ffb21c1
 	if (status != EFI_BUFFER_TOO_SMALL)
ffb21c1
 		return 0;
ffb21c1
@@ -780,10 +793,13 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 	if (!data)
ffb21c1
 		return 0;
ffb21c1
 
ffb21c1
+	spin_lock(&efivars->lock);
ffb21c1
 	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
 					    &var->var.VendorGuid,
ffb21c1
 					    &attributes, &datasize,
ffb21c1
 					    (data + 4));
ffb21c1
+	spin_unlock(&efivars->lock);
ffb21c1
+
ffb21c1
 	if (status != EFI_SUCCESS)
ffb21c1
 		goto out_free;
ffb21c1
 
ffb21c1
@@ -1004,11 +1020,13 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 		/* copied by the above to local storage in the dentry. */
ffb21c1
 		kfree(name);
ffb21c1
 
ffb21c1
+		spin_lock(&efivars->lock);
ffb21c1
 		efivars->ops->get_variable(entry->var.VariableName,
ffb21c1
 					   &entry->var.VendorGuid,
ffb21c1
 					   &entry->var.Attributes,
ffb21c1
 					   &size,
ffb21c1
 					   NULL);
ffb21c1
+		spin_unlock(&efivars->lock);
ffb21c1
 
ffb21c1
 		mutex_lock(&inode->i_mutex);
ffb21c1
 		inode->i_private = entry;
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 90a462e9cf439a1987e0f9434d1f615addcc1970 Mon Sep 17 00:00:00 2001
ffb21c1
From: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Date: Fri, 19 Oct 2012 15:16:45 +0800
ffb21c1
Subject: [PATCH 11/17] efi: Clarify GUID length calculations
ffb21c1
ffb21c1
At present, the handling of GUIDs in efivar file names isn't consistent.
ffb21c1
We use GUID_LEN in some places, and 38 in others (GUID_LEN plus
ffb21c1
separator), and implicitly use the presence of the trailing NUL.
ffb21c1
ffb21c1
This change removes the trailing NUL from GUID_LEN, so that we're
ffb21c1
explicitly adding it when required. We also replace magic numbers
ffb21c1
with GUID_LEN, and clarify the comments where appropriate.
ffb21c1
ffb21c1
We also fix the allocation size in efivar_create_sysfs_entry, where
ffb21c1
we're allocating one byte too much, due to counting the trailing NUL
ffb21c1
twice - once when calculating short_name_size, and once in the kzalloc.
ffb21c1
ffb21c1
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 33 +++++++++++++++++++++++++--------
ffb21c1
 1 file changed, 25 insertions(+), 8 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index d478c56..a93e401 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -95,7 +95,12 @@ MODULE_LICENSE("GPL");
ffb21c1
 MODULE_VERSION(EFIVARS_VERSION);
ffb21c1
 
ffb21c1
 #define DUMP_NAME_LEN 52
ffb21c1
-#define GUID_LEN 37
ffb21c1
+
ffb21c1
+/*
ffb21c1
+ * Length of a GUID string (strlen("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"))
ffb21c1
+ * not including trailing NUL
ffb21c1
+ */
ffb21c1
+#define GUID_LEN 36
ffb21c1
 
ffb21c1
 /*
ffb21c1
  * The maximum size of VariableName + Data = 1024
ffb21c1
@@ -887,7 +892,11 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
 	struct efivar_entry *var;
ffb21c1
 	int namelen, i = 0, err = 0;
ffb21c1
 
ffb21c1
-	if (dentry->d_name.len < 38)
ffb21c1
+	/*
ffb21c1
+	 * We need a GUID, plus at least one letter for the variable name,
ffb21c1
+	 * plus the '-' separator
ffb21c1
+	 */
ffb21c1
+	if (dentry->d_name.len < GUID_LEN + 2)
ffb21c1
 		return -EINVAL;
ffb21c1
 
ffb21c1
 	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
ffb21c1
@@ -900,7 +909,8 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
-	namelen = dentry->d_name.len - GUID_LEN;
ffb21c1
+	/* length of the variable name itself: remove GUID and separator */
ffb21c1
+	namelen = dentry->d_name.len - GUID_LEN - 1;
ffb21c1
 
ffb21c1
 	efivarfs_hex_to_guid(dentry->d_name.name + namelen + 1,
ffb21c1
 			&var->var.VendorGuid);
ffb21c1
@@ -994,8 +1004,8 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 
ffb21c1
 		len = utf16_strlen(entry->var.VariableName);
ffb21c1
 
ffb21c1
-		/* GUID plus trailing NULL */
ffb21c1
-		name = kmalloc(len + 38, GFP_ATOMIC);
ffb21c1
+		/* name, plus '-', plus GUID, plus NUL*/
ffb21c1
+		name = kmalloc(len + 1 + GUID_LEN + 1, GFP_ATOMIC);
ffb21c1
 		if (!name)
ffb21c1
 			goto fail;
ffb21c1
 
ffb21c1
@@ -1006,7 +1016,7 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 
ffb21c1
 		efi_guid_unparse(&entry->var.VendorGuid, name + len + 1);
ffb21c1
 
ffb21c1
-		name[len+GUID_LEN] = '\0';
ffb21c1
+		name[len+GUID_LEN+1] = '\0';
ffb21c1
 
ffb21c1
 		inode = efivarfs_get_inode(efivarfs_sb, root->d_inode,
ffb21c1
 					  S_IFREG | 0644, 0);
ffb21c1
@@ -1435,11 +1445,18 @@ efivar_create_sysfs_entry(struct efivars *efivars,
ffb21c1
 			  efi_char16_t *variable_name,
ffb21c1
 			  efi_guid_t *vendor_guid)
ffb21c1
 {
ffb21c1
-	int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38;
ffb21c1
+	int i, short_name_size;
ffb21c1
 	char *short_name;
ffb21c1
 	struct efivar_entry *new_efivar;
ffb21c1
 
ffb21c1
-	short_name = kzalloc(short_name_size + 1, GFP_KERNEL);
ffb21c1
+	/*
ffb21c1
+	 * Length of the variable bytes in ASCII, plus the '-' separator,
ffb21c1
+	 * plus the GUID, plus trailing NUL
ffb21c1
+	 */
ffb21c1
+	short_name_size = variable_name_size / sizeof(efi_char16_t)
ffb21c1
+				+ 1 + GUID_LEN + 1;
ffb21c1
+
ffb21c1
+	short_name = kzalloc(short_name_size, GFP_KERNEL);
ffb21c1
 	new_efivar = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
ffb21c1
 
ffb21c1
 	if (!short_name || !new_efivar)  {
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From ecbf90823d85ebb41e68e6be01f476862d184825 Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Tue, 16 Oct 2012 15:58:07 +0100
ffb21c1
Subject: [PATCH 12/17] efivarfs: Return an error if we fail to read a
ffb21c1
 variable
ffb21c1
ffb21c1
Instead of always returning 0 in efivarfs_file_read(), even when we
ffb21c1
fail to successfully read the variable, convert the EFI status to
ffb21c1
something meaningful and return that to the caller. This way the user
ffb21c1
will have some hint as to why the read failed.
ffb21c1
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 62 +++++++++++++++++++++++++++-------------------
ffb21c1
 1 file changed, 36 insertions(+), 26 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index a93e401..277e426 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -653,6 +653,36 @@ static int efivarfs_file_open(struct inode *inode, struct file *file)
ffb21c1
 	return 0;
ffb21c1
 }
ffb21c1
 
ffb21c1
+static int efi_status_to_err(efi_status_t status)
ffb21c1
+{
ffb21c1
+	int err;
ffb21c1
+
ffb21c1
+	switch (status) {
ffb21c1
+	case EFI_INVALID_PARAMETER:
ffb21c1
+		err = -EINVAL;
ffb21c1
+		break;
ffb21c1
+	case EFI_OUT_OF_RESOURCES:
ffb21c1
+		err = -ENOSPC;
ffb21c1
+		break;
ffb21c1
+	case EFI_DEVICE_ERROR:
ffb21c1
+		err = -EIO;
ffb21c1
+		break;
ffb21c1
+	case EFI_WRITE_PROTECTED:
ffb21c1
+		err = -EROFS;
ffb21c1
+		break;
ffb21c1
+	case EFI_SECURITY_VIOLATION:
ffb21c1
+		err = -EACCES;
ffb21c1
+		break;
ffb21c1
+	case EFI_NOT_FOUND:
ffb21c1
+		err = -ENOENT;
ffb21c1
+		break;
ffb21c1
+	default:
ffb21c1
+		err = -EINVAL;
ffb21c1
+	}
ffb21c1
+
ffb21c1
+	return err;
ffb21c1
+}
ffb21c1
+
ffb21c1
 static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 		const char __user *userbuf, size_t count, loff_t *ppos)
ffb21c1
 {
ffb21c1
@@ -711,29 +741,7 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 		spin_unlock(&efivars->lock);
ffb21c1
 		kfree(data);
ffb21c1
 
ffb21c1
-		switch (status) {
ffb21c1
-		case EFI_INVALID_PARAMETER:
ffb21c1
-			count = -EINVAL;
ffb21c1
-			break;
ffb21c1
-		case EFI_OUT_OF_RESOURCES:
ffb21c1
-			count = -ENOSPC;
ffb21c1
-			break;
ffb21c1
-		case EFI_DEVICE_ERROR:
ffb21c1
-			count = -EIO;
ffb21c1
-			break;
ffb21c1
-		case EFI_WRITE_PROTECTED:
ffb21c1
-			count = -EROFS;
ffb21c1
-			break;
ffb21c1
-		case EFI_SECURITY_VIOLATION:
ffb21c1
-			count = -EACCES;
ffb21c1
-			break;
ffb21c1
-		case EFI_NOT_FOUND:
ffb21c1
-			count = -ENOENT;
ffb21c1
-			break;
ffb21c1
-		default:
ffb21c1
-			count = -EINVAL;
ffb21c1
-		}
ffb21c1
-		return count;
ffb21c1
+		return efi_status_to_err(status);
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	/*
ffb21c1
@@ -791,12 +799,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 	spin_unlock(&efivars->lock);
ffb21c1
 
ffb21c1
 	if (status != EFI_BUFFER_TOO_SMALL)
ffb21c1
-		return 0;
ffb21c1
+		return efi_status_to_err(status);
ffb21c1
 
ffb21c1
 	data = kmalloc(datasize + 4, GFP_KERNEL);
ffb21c1
 
ffb21c1
 	if (!data)
ffb21c1
-		return 0;
ffb21c1
+		return -ENOMEM;
ffb21c1
 
ffb21c1
 	spin_lock(&efivars->lock);
ffb21c1
 	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
@@ -805,8 +813,10 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 					    (data + 4));
ffb21c1
 	spin_unlock(&efivars->lock);
ffb21c1
 
ffb21c1
-	if (status != EFI_SUCCESS)
ffb21c1
+	if (status != EFI_SUCCESS) {
ffb21c1
+		size = efi_status_to_err(status);
ffb21c1
 		goto out_free;
ffb21c1
+	}
ffb21c1
 
ffb21c1
 	memcpy(data, &attributes, 4);
ffb21c1
 	size = simple_read_from_buffer(userbuf, count, ppos,
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 39210330739b943856ff21b29b4a0804f4e8349f Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Mon, 22 Oct 2012 15:23:29 +0100
ffb21c1
Subject: [PATCH 13/17] efivarfs: Replace magic number with sizeof(attributes)
ffb21c1
ffb21c1
Seeing "+ 4" littered throughout the functions gets a bit
ffb21c1
confusing. Use "sizeof(attributes)" which clearly explains what
ffb21c1
quantity we're adding.
ffb21c1
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 8 ++++----
ffb21c1
 1 file changed, 4 insertions(+), 4 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 277e426..2c04434 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -801,7 +801,7 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 	if (status != EFI_BUFFER_TOO_SMALL)
ffb21c1
 		return efi_status_to_err(status);
ffb21c1
 
ffb21c1
-	data = kmalloc(datasize + 4, GFP_KERNEL);
ffb21c1
+	data = kmalloc(datasize + sizeof(attributes), GFP_KERNEL);
ffb21c1
 
ffb21c1
 	if (!data)
ffb21c1
 		return -ENOMEM;
ffb21c1
@@ -810,7 +810,7 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 	status = efivars->ops->get_variable(var->var.VariableName,
ffb21c1
 					    &var->var.VendorGuid,
ffb21c1
 					    &attributes, &datasize,
ffb21c1
-					    (data + 4));
ffb21c1
+					    (data + sizeof(attributes)));
ffb21c1
 	spin_unlock(&efivars->lock);
ffb21c1
 
ffb21c1
 	if (status != EFI_SUCCESS) {
ffb21c1
@@ -818,9 +818,9 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
 		goto out_free;
ffb21c1
 	}
ffb21c1
 
ffb21c1
-	memcpy(data, &attributes, 4);
ffb21c1
+	memcpy(data, &attributes, sizeof(attributes));
ffb21c1
 	size = simple_read_from_buffer(userbuf, count, ppos,
ffb21c1
-					data, datasize + 4);
ffb21c1
+				       data, datasize + sizeof(attributes));
ffb21c1
 out_free:
ffb21c1
 	kfree(data);
ffb21c1
 
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 5555f0af6294b3675a95a06da23101150644936d Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Mon, 22 Oct 2012 15:51:45 +0100
ffb21c1
Subject: [PATCH 14/17] efivarfs: Add unique magic number
ffb21c1
ffb21c1
Using pstore's superblock magic number is no doubt going to cause
ffb21c1
problems in the future. Give efivarfs its own magic number.
ffb21c1
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 2 +-
ffb21c1
 include/uapi/linux/magic.h | 1 +
ffb21c1
 2 files changed, 2 insertions(+), 1 deletion(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 2c04434..3b0cf9a 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -991,7 +991,7 @@ int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
ffb21c1
 	sb->s_maxbytes          = MAX_LFS_FILESIZE;
ffb21c1
 	sb->s_blocksize         = PAGE_CACHE_SIZE;
ffb21c1
 	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
ffb21c1
-	sb->s_magic             = PSTOREFS_MAGIC;
ffb21c1
+	sb->s_magic             = EFIVARFS_MAGIC;
ffb21c1
 	sb->s_op                = &efivarfs_ops;
ffb21c1
 	sb->s_time_gran         = 1;
ffb21c1
 
ffb21c1
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
ffb21c1
index e15192c..12f68c7 100644
ffb21c1
--- a/include/uapi/linux/magic.h
ffb21c1
+++ b/include/uapi/linux/magic.h
ffb21c1
@@ -27,6 +27,7 @@
ffb21c1
 #define ISOFS_SUPER_MAGIC	0x9660
ffb21c1
 #define JFFS2_SUPER_MAGIC	0x72b6
ffb21c1
 #define PSTOREFS_MAGIC		0x6165676C
ffb21c1
+#define EFIVARFS_MAGIC		0xde5e81e4
ffb21c1
 
ffb21c1
 #define MINIX_SUPER_MAGIC	0x137F		/* minix v1 fs, 14 char names */
ffb21c1
 #define MINIX_SUPER_MAGIC2	0x138F		/* minix v1 fs, 30 char names */
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From a42845c67f2386b164d0c5d8220838d7faf5a409 Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Tue, 23 Oct 2012 12:35:43 +0100
ffb21c1
Subject: [PATCH 15/17] efivarfs: Make 'datasize' unsigned long
ffb21c1
ffb21c1
There's no reason to declare 'datasize' as an int, since the majority
ffb21c1
of the functions it's passed to expect an unsigned long anyway. Plus,
ffb21c1
this way we avoid any sign problems during arithmetic.
ffb21c1
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 2 +-
ffb21c1
 1 file changed, 1 insertion(+), 1 deletion(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 3b0cf9a..6a858d1 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -692,7 +692,7 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 	void *data;
ffb21c1
 	u32 attributes;
ffb21c1
 	struct inode *inode = file->f_mapping->host;
ffb21c1
-	int datasize = count - sizeof(attributes);
ffb21c1
+	unsigned long datasize = count - sizeof(attributes);
ffb21c1
 	unsigned long newdatasize;
ffb21c1
 
ffb21c1
 	if (count < sizeof(attributes))
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From a268bdf6d7ce623ea4bdfcf39aa52ed3fbfdfd65 Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Tue, 23 Oct 2012 12:41:03 +0100
ffb21c1
Subject: [PATCH 16/17] efivarfs: Return a consistent error when
ffb21c1
 efivarfs_get_inode() fails
ffb21c1
ffb21c1
Instead of returning -ENOSPC if efivarfs_get_inode() fails we should
ffb21c1
be returning -ENOMEM, since running out of memory is the only reason
ffb21c1
it can fail.  Furthermore, that's the error value used everywhere else
ffb21c1
in this file. It's also less likely to confuse users that hit this
ffb21c1
error case.
ffb21c1
ffb21c1
Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 2 +-
ffb21c1
 1 file changed, 1 insertion(+), 1 deletion(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 6a858d1..58cec62 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -911,7 +911,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
ffb21c1
 
ffb21c1
 	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
ffb21c1
 	if (!inode)
ffb21c1
-		return -ENOSPC;
ffb21c1
+		return -ENOMEM;
ffb21c1
 
ffb21c1
 	var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
ffb21c1
 	if (!var) {
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1
ffb21c1
ffb21c1
From 9c3136c987175b179c0aa725d76cda156894f918 Mon Sep 17 00:00:00 2001
ffb21c1
From: Matt Fleming <matt.fleming@intel.com>
ffb21c1
Date: Fri, 26 Oct 2012 12:18:53 +0100
ffb21c1
Subject: [PATCH 17/17] efivarfs: Fix return value of efivarfs_file_write()
ffb21c1
ffb21c1
We're stuffing a variable of type size_t (unsigned) into a ssize_t
ffb21c1
(signed) which, even though both types should be the same number of
ffb21c1
bits, it's just asking for sign issues to be introduced.
ffb21c1
ffb21c1
Cc: Jeremy Kerr <jeremy.kerr@canonical.com>
ffb21c1
Reported-by: Alan Cox <alan@lxorguk.ukuu.org.uk>
ffb21c1
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
ffb21c1
---
ffb21c1
 drivers/firmware/efivars.c | 13 ++++++++-----
ffb21c1
 1 file changed, 8 insertions(+), 5 deletions(-)
ffb21c1
ffb21c1
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
ffb21c1
index 58cec62..9ac9340 100644
ffb21c1
--- a/drivers/firmware/efivars.c
ffb21c1
+++ b/drivers/firmware/efivars.c
ffb21c1
@@ -694,6 +694,7 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 	struct inode *inode = file->f_mapping->host;
ffb21c1
 	unsigned long datasize = count - sizeof(attributes);
ffb21c1
 	unsigned long newdatasize;
ffb21c1
+	ssize_t bytes = 0;
ffb21c1
 
ffb21c1
 	if (count < sizeof(attributes))
ffb21c1
 		return -EINVAL;
ffb21c1
@@ -706,22 +707,22 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 	efivars = var->efivars;
ffb21c1
 
ffb21c1
 	if (copy_from_user(&attributes, userbuf, sizeof(attributes))) {
ffb21c1
-		count = -EFAULT;
ffb21c1
+		bytes = -EFAULT;
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	if (attributes & ~(EFI_VARIABLE_MASK)) {
ffb21c1
-		count = -EINVAL;
ffb21c1
+		bytes = -EINVAL;
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) {
ffb21c1
-		count = -EFAULT;
ffb21c1
+		bytes = -EFAULT;
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
 	if (validate_var(&var->var, data, datasize) == false) {
ffb21c1
-		count = -EINVAL;
ffb21c1
+		bytes = -EINVAL;
ffb21c1
 		goto out;
ffb21c1
 	}
ffb21c1
 
ffb21c1
@@ -744,6 +745,8 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 		return efi_status_to_err(status);
ffb21c1
 	}
ffb21c1
 
ffb21c1
+	bytes = count;
ffb21c1
+
ffb21c1
 	/*
ffb21c1
 	 * Writing to the variable may have caused a change in size (which
ffb21c1
 	 * could either be an append or an overwrite), or the variable to be
ffb21c1
@@ -778,7 +781,7 @@ static ssize_t efivarfs_file_write(struct file *file,
ffb21c1
 out:
ffb21c1
 	kfree(data);
ffb21c1
 
ffb21c1
-	return count;
ffb21c1
+	return bytes;
ffb21c1
 }
ffb21c1
 
ffb21c1
 static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
ffb21c1
-- 
ffb21c1
1.7.12.1
ffb21c1