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