[opensuse-kernel] [PATCH 0/19] Backported patches for support UEFI variable filesystem
Patch-mainline: v3.8-rc1..v3.8-rc3 Target: openSUSE 12.3 Test steps: + build; make modules_install; make install + mount -t efivarfs none /sys/firmware/efi/efivars/ or create file /lib/systemd/system/sys-firmware-efi-efivars.mount [1] + ls /sys/firmware/efi/efivars will show up all EFI variables + Try the small create[2]/delete[3] programs from Gary Lin The create program will create a EFI variable is TestVar, then we can see it show up in /sys/firmware/efi/efivars. And, delete program can remove it. Backported 19 patches: 0001-efi-Add-support-for-a-UEFI-variable-filesystem.patch 0002-efi-Handle-deletions-and-size-changes-in-efivarfs_w.patch 0003-efi-add-efivars-kobject-to-efi-sysfs-folder.patch 0004-efivarfs-Add-documentation-for-the-EFI-variable-fil.patch 0005-efivarfs-efivarfs_file_read-ensure-we-free-data-in.patch 0006-efivarfs-efivarfs_create-ensure-we-drop-our-refer.patch 0007-efivarfs-efivarfs_fill_super-fix-inode-reference.patch 0008-efivarfs-efivarfs_fill_super-ensure-we-free-our-t.patch 0009-efivarfs-efivarfs_fill_super-ensure-we-clean-up-c.patch 0010-efivarfs-Implement-exclusive-access-for-get-set-_v.patch 0011-efivarfs-Return-an-error-if-we-fail-to-read-a-variab.patch 0012-efi-Clarify-GUID-length-calculations.patch 0013-efivarfs-Replace-magic-number-with-sizeof-attributes.patch 0014-efivarfs-Add-unique-magic-number.patch 0015-efivarfs-Make-datasize-unsigned-long.patch 0016-efivarfs-Return-a-consistent-error-when-efivarfs_get.patch 0017-efivarfs-Fix-return-value-of-efivarfs_file_write.patch 0018-efivarfs-Use-query_variable_info-to-limit-kmalloc.patch 0019-efivarfs-Make-efivarfs_fill_super-static.patch [1] /lib/systemd/system/sys-firmware-efi-efivars.mount (already sent to systemd mailing list for review) [Unit] Description=EFI Variables File System Documentation=https://www.kernel.org/doc/Documentation/filesystems/efivarfs.txt DefaultDependencies=no ConditionPathExists=/sys/firmware/efi/efivars Before=sysinit.target [Mount] What=efivarfs Where=/sys/firmware/efi/efivars Type=efivarfs [2] create.c #include <stdio.h> #include <string.h> #include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <linux/limits.h> #include "def.h" int main () { const char *variable_name = "TestVar"; char file_path[PATH_MAX]; int fd, flags; mode_t mode; uint32_t attribute; char buffer[1024 + 4]; int i; snprintf (file_path, PATH_MAX, "%s%s-%s", EFIVARS_FS, variable_name, MY_GUID); flags = O_CREAT | O_WRONLY; mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; fd = open (file_path, flags, mode); if (fd < 0) { fprintf (stderr, "Failed to open %s\n", file_path); return -1; } attribute = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; memcpy (buffer, &attribute, sizeof(uint32_t)); for (i = 0; i < 1024; i++) buffer[i+4] = 'a'; if (write (fd, buffer, 1024 + 4) != (1024 + 4)) { fprintf (stderr, "Failed to write\n"); } close (fd); return 0; } [3] delete.c #include <stdio.h> #include <unistd.h> #include <linux/limits.h> #include "def.h" int main () { const char *variable_name = "TestVar"; char file_path[PATH_MAX]; snprintf (file_path, PATH_MAX, "%s%s-%s", EFIVARS_FS, variable_name, MY_GUID); unlink (file_path); return 0; } -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matthew Garrett <mjg@redhat.com> Git-commit: 5d9db883761ad1bc2245fd3018715549b974203d Patch-mainline: v3.8-rc1 References: fate#314499, fate#314509 Target: SLE-11 SP3 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 <jeremy.kerr@canonical.com>. Signed-off-by: Matthew Garrett <mjg@redhat.com> Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 384 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/efi.h | 5 2 files changed, 383 insertions(+), 6 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -80,6 +80,10 @@ #include <linux/slab.h> #include <linux/pstore.h> +#include <linux/fs.h> +#include <linux/ramfs.h> +#include <linux/pagemap.h> + #include <asm/uaccess.h> #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 = default_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 *efi 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 --- 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; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Jeremy Kerr <jeremy.kerr@canonical.com> Git-commit: 0c542edde3cecc99b180a440ae33dcb7f28642ce Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 49 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -658,6 +658,7 @@ static ssize_t efivarfs_file_write(struc 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(struc 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); -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Lee, Chun-Yi <joeyli.kernel@gmail.com> Git-commit: 605e70c7aa1b7b0d554baf945630c1d606bbfbc3 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <mjg@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Lee, Chun-Yi <jlee@suse.com> Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> --- drivers/firmware/efivars.c | 9 +++++++++ include/linux/efi.h | 1 + 2 files changed, 10 insertions(+) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -1527,6 +1527,7 @@ void unregister_efivars(struct 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 *efi 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. --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -699,6 +699,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; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: e913ca7d16d70b75367ff56a3b201980501d542c Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- Documentation/filesystems/00-INDEX | 2 ++ Documentation/filesystems/efivarfs.txt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 Documentation/filesystems/efivarfs.txt --- 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 --- /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 -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Andy Whitcroft <apw@canonical.com> Git-commit: d142df03a798ee7d2db10a1f20945110ea6067ff Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 Signed-off-by: Andy Whitcroft <apw@canonical.com> Acked-by: Matthew Garrett <mjg@redhat.com> Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -766,7 +766,7 @@ static ssize_t efivarfs_file_read(struct 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 &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; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Andy Whitcroft <apw@canonical.com> Git-commit: 45a937a883c4411648b80e87341b237cc48009c1 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 Signed-off-by: Andy Whitcroft <apw@canonical.com> Acked-by: Matthew Garrett <mjg@redhat.com> Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -866,7 +866,7 @@ static void efivarfs_hex_to_guid(const c static int efivarfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) { - 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 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 d_instantiate(dentry, inode); dget(dentry); out: - if (err) + if (err) { kfree(var); + iput(inode); + } return err; } -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Andy Whitcroft <apw@canonical.com> Git-commit: 5c9b50ab8ca962bc085cfb49bae947910d73f794 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <apw@canonical.com> Acked-by: Matthew Garrett <mjg@redhat.com> Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -948,7 +948,6 @@ int efivarfs_fill_super(struct super_blo 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_blo 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_blo } return 0; -fail: - iput(inode); - return err; } static struct dentry *efivarfs_mount(struct file_system_type *fs_type, -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Andy Whitcroft <apw@canonical.com> Git-commit: c0359db1a1c484ea810f4d5a877d3b00d107908b Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 d_alloc_name() copies the passed name to new storage, once complete we no longer need our name. Signed-off-by: Andy Whitcroft <apw@canonical.com> Acked-by: Matthew Garrett <mjg@redhat.com> Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 2 ++ 1 file changed, 2 insertions(+) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -992,6 +992,8 @@ int efivarfs_fill_super(struct super_blo 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, -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Andy Whitcroft <apw@canonical.com> Git-commit: 5ba6e2919b9e18a051e5bdd6c21f52ee4976513f Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 Ensure we free both the name and inode on error when building the individual variables. Signed-off-by: Andy Whitcroft <apw@canonical.com> Acked-by: Matthew Garrett <mjg@redhat.com> Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -948,6 +948,7 @@ int efivarfs_fill_super(struct super_blo 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_blo 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_blo 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_blo } return 0; + +fail_inode: + iput(inode); +fail_name: + kfree(name); +fail: + return -ENOMEM; } static struct dentry *efivarfs_mount(struct file_system_type *fs_type, -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Jeremy Kerr <jeremy.kerr@canonical.com> Git-commit: f5f6a60ad52fc786c517303590f1efaea614c69b Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 68 ++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 25 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -690,35 +690,45 @@ static ssize_t efivarfs_file_write(struc 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(struc 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(struc 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 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 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_blo /* 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; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: 7253eaba7b179db2e07e67c5b78d5f10b332291d Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 62 ++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 26 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -648,6 +648,36 @@ static int efivarfs_file_open(struct ino 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) { @@ -706,29 +736,7 @@ static ssize_t efivarfs_file_write(struc 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); } /* @@ -786,12 +794,12 @@ static ssize_t efivarfs_file_read(struct 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, @@ -800,8 +808,10 @@ static ssize_t efivarfs_file_read(struct (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, -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Jeremy Kerr <jeremy.kerr@canonical.com> Git-commit: 310ad75448329f167af3c4a10e4a9de4d80058ff Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) --- 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 @@ -897,7 +902,11 @@ static int efivarfs_create(struct inode 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); @@ -910,7 +919,8 @@ static int efivarfs_create(struct inode 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); @@ -1004,8 +1014,8 @@ int efivarfs_fill_super(struct super_blo 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; @@ -1016,7 +1026,7 @@ int efivarfs_fill_super(struct super_blo 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); @@ -1445,11 +1455,18 @@ efivar_create_sysfs_entry(struct 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) { -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: d292384152fdd465dba792d555ce5db83e94efa0 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -801,7 +801,7 @@ static ssize_t efivarfs_file_read(struct 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 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 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); -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: 91716322d834cba34f4a7ed5e4a39673eb90862b Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 2 +- include/uapi/linux/magic.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -991,7 +991,7 @@ int efivarfs_fill_super(struct super_blo 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; --- 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 */ -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: 07b1c5bc64cff9c880261a1fef562ef7ea7f6575 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -692,7 +692,7 @@ static ssize_t efivarfs_file_write(struc 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)) -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: aeeaa8d46aa38c9cc5fac23feb9b1f91bdbf5dd3 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -911,7 +911,7 @@ static int efivarfs_create(struct inode 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) { -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: cfcf2f11708f934d2bd294f973c2fcb0cc54f293 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 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 <jeremy.kerr@canonical.com> Reported-by: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -694,6 +694,7 @@ static ssize_t efivarfs_file_write(struc 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(struc 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(struc 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(struc out: kfree(data); - return count; + return bytes; } static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: 89d16665d388837b30972081d97b814be26d68a2 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 We don't want someone who can write EFI variables to be able to allocate arbitrarily large amounts of memory, so cap it to something sensible like the amount of free space for EFI variables. Acked-by: Jeremy Kerr <jeremy.kerr@canonical.com> Cc: Matthew Garrett <mjg@redhat.com> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 43 ++++++++++++++++++++++++++++++++++--------- include/linux/efi.h | 1 + 2 files changed, 35 insertions(+), 9 deletions(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -694,28 +694,51 @@ static ssize_t efivarfs_file_write(struc struct inode *inode = file->f_mapping->host; unsigned long datasize = count - sizeof(attributes); unsigned long newdatasize; + u64 storage_size, remaining_size, max_size; ssize_t bytes = 0; if (count < sizeof(attributes)) return -EINVAL; - data = kmalloc(datasize, GFP_KERNEL); + if (copy_from_user(&attributes, userbuf, sizeof(attributes))) + return -EFAULT; - if (!data) - return -ENOMEM; + if (attributes & ~(EFI_VARIABLE_MASK)) + return -EINVAL; efivars = var->efivars; - if (copy_from_user(&attributes, userbuf, sizeof(attributes))) { - bytes = -EFAULT; - goto out; + /* + * Ensure that the user can't allocate arbitrarily large + * amounts of memory. Pick a default size of 64K if + * QueryVariableInfo() isn't supported by the firmware. + */ + spin_lock(&efivars->lock); + + if (!efivars->ops->query_variable_info) + status = EFI_UNSUPPORTED; + else { + const struct efivar_operations *fops = efivars->ops; + status = fops->query_variable_info(attributes, &storage_size, + &remaining_size, &max_size); } - if (attributes & ~(EFI_VARIABLE_MASK)) { - bytes = -EINVAL; - goto out; + spin_unlock(&efivars->lock); + + if (status != EFI_SUCCESS) { + if (status != EFI_UNSUPPORTED) + return efi_status_to_err(status); + + remaining_size = 65536; } + if (datasize > remaining_size) + return -ENOSPC; + + data = kmalloc(datasize, GFP_KERNEL); + if (!data) + return -ENOMEM; + if (copy_from_user(data, userbuf + sizeof(attributes), datasize)) { bytes = -EFAULT; goto out; @@ -1709,6 +1732,8 @@ efivars_init(void) ops.get_variable = efi.get_variable; ops.set_variable = efi.set_variable; ops.get_next_variable = efi.get_next_variable; + ops.query_variable_info = efi.query_variable_info; + error = register_efivars(&__efivars, &ops, efi_kobj); if (error) goto err_put; --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -685,6 +685,7 @@ struct efivar_operations { efi_get_variable_t *get_variable; efi_get_next_variable_t *get_next_variable; efi_set_variable_t *set_variable; + efi_query_variable_info_t *query_variable_info; }; struct efivars { -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
From: Matt Fleming <matt.fleming@intel.com> Git-commit: e83af1f18c78c7b6aa720beecc927ecc8afd3647 Patch-mainline: v3.8-rc1 Target: openSUSE 12.3 sparse is complaining that efivarfs_fill_super() doesn't have a prototype. Make it static to avoid the warning. Cc: Xie ChanglongX <changlongx.xie@intel.com> Cc: Matthew Garrett <mjg@redhat.com> Cc: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com> Acked-by: Lee, Chun-Yi <jlee@suse.com> --- drivers/firmware/efivars.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -1004,7 +1004,7 @@ static int efivarfs_unlink(struct inode return -EINVAL; }; -int efivarfs_fill_super(struct super_block *sb, void *data, int silent) +static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode = NULL; struct dentry *root; -- To unsubscribe, e-mail: opensuse-kernel+unsubscribe@opensuse.org To contact the owner, e-mail: opensuse-kernel+owner@opensuse.org
participants (1)
-
Lee, Chun-Yi