diff options
Diffstat (limited to 'drivers/firmware/efivars.c')
-rw-r--r-- | drivers/firmware/efivars.c | 501 |
1 files changed, 119 insertions, 382 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index b07cb37..f5596db 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -79,7 +79,6 @@ #include <linux/device.h> #include <linux/slab.h> #include <linux/pstore.h> -#include <linux/ctype.h> #include <linux/fs.h> #include <linux/ramfs.h> @@ -103,11 +102,6 @@ MODULE_VERSION(EFIVARS_VERSION); */ #define GUID_LEN 36 -static bool efivars_pstore_disable = - IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE); - -module_param_named(pstore_disable, efivars_pstore_disable, bool, 0644); - /* * The maximum size of VariableName + Data = 1024 * Therefore, it's reasonable to save that much @@ -411,11 +405,10 @@ static efi_status_t get_var_data(struct efivars *efivars, struct efi_variable *var) { efi_status_t status; - unsigned long flags; - spin_lock_irqsave(&efivars->lock, flags); + spin_lock(&efivars->lock); status = get_var_data_locked(efivars, var); - spin_unlock_irqrestore(&efivars->lock, flags); + spin_unlock(&efivars->lock); if (status != EFI_SUCCESS) { printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", @@ -424,44 +417,6 @@ get_var_data(struct efivars *efivars, struct efi_variable *var) return status; } -static efi_status_t -check_var_size_locked(struct efivars *efivars, u32 attributes, - unsigned long size) -{ - u64 storage_size, remaining_size, max_size; - efi_status_t status; - const struct efivar_operations *fops = efivars->ops; - - if (!efivars->ops->query_variable_info) - return EFI_UNSUPPORTED; - - status = fops->query_variable_info(attributes, &storage_size, - &remaining_size, &max_size); - - if (status != EFI_SUCCESS) - return status; - - if (!storage_size || size > remaining_size || size > max_size || - (remaining_size - size) < (storage_size / 2)) - return EFI_OUT_OF_RESOURCES; - - return status; -} - - -static efi_status_t -check_var_size(struct efivars *efivars, u32 attributes, unsigned long size) -{ - efi_status_t status; - unsigned long flags; - - spin_lock_irqsave(&efivars->lock, flags); - status = check_var_size_locked(efivars, attributes, size); - spin_unlock_irqrestore(&efivars->lock, flags); - - return status; -} - static ssize_t efivar_guid_read(struct efivar_entry *entry, char *buf) { @@ -582,19 +537,14 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) return -EINVAL; } - spin_lock_irq(&efivars->lock); - - status = check_var_size_locked(efivars, new_var->Attributes, - new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); - - if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED) - status = efivars->ops->set_variable(new_var->VariableName, - &new_var->VendorGuid, - new_var->Attributes, - new_var->DataSize, - new_var->Data); + spin_lock(&efivars->lock); + status = efivars->ops->set_variable(new_var->VariableName, + &new_var->VendorGuid, + new_var->Attributes, + new_var->DataSize, + new_var->Data); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); if (status != EFI_SUCCESS) { printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", @@ -743,7 +693,8 @@ static ssize_t efivarfs_file_write(struct file *file, u32 attributes; struct inode *inode = file->f_mapping->host; unsigned long datasize = count - sizeof(attributes); - unsigned long newdatasize, varsize; + unsigned long newdatasize; + u64 storage_size, remaining_size, max_size; ssize_t bytes = 0; if (count < sizeof(attributes)) @@ -762,18 +713,28 @@ static ssize_t efivarfs_file_write(struct file *file, * 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); + } - varsize = datasize + utf16_strsize(var->var.VariableName, 1024); - status = check_var_size(efivars, attributes, varsize); + spin_unlock(&efivars->lock); if (status != EFI_SUCCESS) { if (status != EFI_UNSUPPORTED) return efi_status_to_err(status); - if (datasize > 65536) - return -ENOSPC; + remaining_size = 65536; } + if (datasize > remaining_size) + return -ENOSPC; + data = kmalloc(datasize, GFP_KERNEL); if (!data) return -ENOMEM; @@ -793,20 +754,7 @@ static ssize_t efivarfs_file_write(struct file *file, * set_variable call, and removal of the variable from the efivars * list (in the case of an authenticated delete). */ - spin_lock_irq(&efivars->lock); - - /* - * Ensure that the available space hasn't shrunk below the safe level - */ - - status = check_var_size_locked(efivars, attributes, varsize); - - if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) { - spin_unlock_irq(&efivars->lock); - kfree(data); - - return efi_status_to_err(status); - } + spin_lock(&efivars->lock); status = efivars->ops->set_variable(var->var.VariableName, &var->var.VendorGuid, @@ -814,7 +762,7 @@ static ssize_t efivarfs_file_write(struct file *file, data); if (status != EFI_SUCCESS) { - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); kfree(data); return efi_status_to_err(status); @@ -835,21 +783,21 @@ static ssize_t efivarfs_file_write(struct file *file, NULL); if (status == EFI_BUFFER_TOO_SMALL) { - spin_unlock_irq(&efivars->lock); + 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) { list_del(&var->list); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); efivar_unregister(var); drop_nlink(inode); d_delete(file->f_dentry); dput(file->f_dentry); } else { - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); pr_warn("efivarfs: inconsistent EFI variable implementation? " "status = %lx\n", status); } @@ -871,11 +819,11 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, void *data; ssize_t size = 0; - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); status = efivars->ops->get_variable(var->var.VariableName, &var->var.VendorGuid, &attributes, &datasize, NULL); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); if (status != EFI_BUFFER_TOO_SMALL) return efi_status_to_err(status); @@ -885,12 +833,12 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf, if (!data) return -ENOMEM; - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); status = efivars->ops->get_variable(var->var.VariableName, &var->var.VendorGuid, &attributes, &datasize, (data + sizeof(attributes))); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); if (status != EFI_SUCCESS) { size = efi_status_to_err(status); @@ -952,48 +900,6 @@ static struct inode *efivarfs_get_inode(struct super_block *sb, return inode; } -/* - * Return true if 'str' is a valid efivarfs filename of the form, - * - * VariableName-12345678-1234-1234-1234-1234567891bc - */ -static bool efivarfs_valid_name(const char *str, int len) -{ - static const char dashes[GUID_LEN] = { - [8] = 1, [13] = 1, [18] = 1, [23] = 1 - }; - const char *s = str + len - GUID_LEN; - int i; - - /* - * We need a GUID, plus at least one letter for the variable name, - * plus the '-' separator - */ - if (len < GUID_LEN + 2) - return false; - - /* GUID must be preceded by a '-' */ - if (*(s - 1) != '-') - return false; - - /* - * Validate that 's' is of the correct format, e.g. - * - * 12345678-1234-1234-1234-123456789abc - */ - for (i = 0; i < GUID_LEN; i++) { - if (dashes[i]) { - if (*s++ != '-') - return false; - } else { - if (!isxdigit(*s++)) - return false; - } - } - - return true; -} - 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]); @@ -1022,7 +928,11 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, struct efivar_entry *var; int namelen, i = 0, err = 0; - if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) + /* + * 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); @@ -1056,9 +966,9 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, goto out; kobject_uevent(&var->kobj, KOBJ_ADD); - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); list_add(&var->list, &efivars->list); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); d_instantiate(dentry, inode); dget(dentry); out: @@ -1075,7 +985,7 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) struct efivars *efivars = var->efivars; efi_status_t status; - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); status = efivars->ops->set_variable(var->var.VariableName, &var->var.VendorGuid, @@ -1083,102 +993,17 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) if (status == EFI_SUCCESS || status == EFI_NOT_FOUND) { list_del(&var->list); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); efivar_unregister(var); drop_nlink(dentry->d_inode); dput(dentry); return 0; } - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return -EINVAL; }; -/* - * Compare two efivarfs file names. - * - * An efivarfs filename is composed of two parts, - * - * 1. A case-sensitive variable name - * 2. A case-insensitive GUID - * - * So we need to perform a case-sensitive match on part 1 and a - * case-insensitive match on part 2. - */ -static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, - unsigned int len, const char *str, - const struct qstr *name) -{ - int guid = len - GUID_LEN; - - if (name->len != len) - return 1; - - /* Case-sensitive compare for the variable name */ - if (memcmp(str, name->name, guid)) - return 1; - - /* Case-insensitive compare for the GUID */ - return strncasecmp(name->name + guid, str + guid, GUID_LEN); -} - -static int efivarfs_d_hash(const struct dentry *dentry, - const struct inode *inode, struct qstr *qstr) -{ - unsigned long hash = init_name_hash(); - const unsigned char *s = qstr->name; - unsigned int len = qstr->len; - - if (!efivarfs_valid_name(s, len)) - return -EINVAL; - - while (len-- > GUID_LEN) - hash = partial_name_hash(*s++, hash); - - /* GUID is case-insensitive. */ - while (len--) - hash = partial_name_hash(tolower(*s++), hash); - - qstr->hash = end_name_hash(hash); - return 0; -} - -/* - * Retaining negative dentries for an in-memory filesystem just wastes - * memory and lookup time: arrange for them to be deleted immediately. - */ -static int efivarfs_delete_dentry(const struct dentry *dentry) -{ - return 1; -} - -static struct dentry_operations efivarfs_d_ops = { - .d_compare = efivarfs_d_compare, - .d_hash = efivarfs_d_hash, - .d_delete = efivarfs_delete_dentry, -}; - -static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) -{ - struct dentry *d; - struct qstr q; - int err; - - q.name = name; - q.len = strlen(name); - - err = efivarfs_d_hash(NULL, NULL, &q); - if (err) - return ERR_PTR(err); - - d = d_alloc(parent, &q); - if (d) - return d; - - return ERR_PTR(-ENOMEM); -} - static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode = NULL; @@ -1186,7 +1011,6 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) struct efivar_entry *entry, *n; struct efivars *efivars = &__efivars; char *name; - int err = -ENOMEM; efivarfs_sb = sb; @@ -1195,7 +1019,6 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = EFIVARFS_MAGIC; sb->s_op = &efivarfs_ops; - sb->s_d_op = &efivarfs_d_ops; sb->s_time_gran = 1; inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); @@ -1236,22 +1059,20 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) if (!inode) goto fail_name; - dentry = efivarfs_alloc_dentry(root, name); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); + dentry = d_alloc_name(root, name); + if (!dentry) goto fail_inode; - } /* copied by the above to local storage in the dentry. */ kfree(name); - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); efivars->ops->get_variable(entry->var.VariableName, &entry->var.VendorGuid, &entry->var.Attributes, &size, NULL); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); mutex_lock(&inode->i_mutex); inode->i_private = entry; @@ -1267,7 +1088,7 @@ fail_inode: fail_name: kfree(name); fail: - return err; + return -ENOMEM; } static struct dentry *efivarfs_mount(struct file_system_type *fs_type, @@ -1288,31 +1109,21 @@ static struct file_system_type efivarfs_type = { .kill_sb = efivarfs_kill_sb, }; -/* - * Handle negative dentry. - */ -static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - if (dentry->d_name.len > NAME_MAX) - return ERR_PTR(-ENAMETOOLONG); - d_add(dentry, NULL); - return NULL; -} - static const struct inode_operations efivarfs_dir_inode_operations = { - .lookup = efivarfs_lookup, + .lookup = simple_lookup, .unlink = efivarfs_unlink, .create = efivarfs_create, }; -#ifdef CONFIG_EFI_VARS_PSTORE +static struct pstore_info efi_pstore_info; + +#ifdef CONFIG_PSTORE static int efi_pstore_open(struct pstore_info *psi) { struct efivars *efivars = psi->data; - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); efivars->walk_entry = list_first_entry(&efivars->list, struct efivar_entry, list); return 0; @@ -1322,7 +1133,7 @@ static int efi_pstore_close(struct pstore_info *psi) { struct efivars *efivars = psi->data; - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return 0; } @@ -1396,22 +1207,22 @@ static int efi_pstore_write(enum pstore_type_id type, efi_guid_t vendor = LINUX_EFI_CRASH_GUID; struct efivars *efivars = psi->data; int i, ret = 0; + u64 storage_space, remaining_space, max_variable_size; efi_status_t status = EFI_NOT_FOUND; - unsigned long flags; - spin_lock_irqsave(&efivars->lock, flags); + spin_lock(&efivars->lock); /* * Check if there is a space enough to log. * size: a size of logging data * DUMP_NAME_LEN * 2: a maximum size of variable name */ - - status = check_var_size_locked(efivars, PSTORE_EFI_ATTRIBUTES, - size + DUMP_NAME_LEN * 2); - - if (status) { - spin_unlock_irqrestore(&efivars->lock, flags); + status = efivars->ops->query_variable_info(PSTORE_EFI_ATTRIBUTES, + &storage_space, + &remaining_space, + &max_variable_size); + if (status || remaining_space < size + DUMP_NAME_LEN * 2) { + spin_unlock(&efivars->lock); *id = part; return -ENOSPC; } @@ -1425,7 +1236,7 @@ static int efi_pstore_write(enum pstore_type_id type, efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES, size, psi->buf); - spin_unlock_irqrestore(&efivars->lock, flags); + spin_unlock(&efivars->lock); if (size) ret = efivar_create_sysfs_entry(efivars, @@ -1452,7 +1263,7 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, sprintf(name, "dump-type%u-%u-%d-%lu", type, (unsigned int)id, count, time.tv_sec); - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); for (i = 0; i < DUMP_NAME_LEN; i++) efi_name[i] = name[i]; @@ -1496,13 +1307,45 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, if (found) list_del(&found->list); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); if (found) efivar_unregister(found); return 0; } +#else +static int efi_pstore_open(struct pstore_info *psi) +{ + return 0; +} + +static int efi_pstore_close(struct pstore_info *psi) +{ + return 0; +} + +static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, int *count, + struct timespec *timespec, + char **buf, struct pstore_info *psi) +{ + return -1; +} + +static int efi_pstore_write(enum pstore_type_id type, + enum kmsg_dump_reason reason, u64 *id, + unsigned int part, int count, size_t size, + struct pstore_info *psi) +{ + return 0; +} + +static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, + struct timespec time, struct pstore_info *psi) +{ + return 0; +} +#endif static struct pstore_info efi_pstore_info = { .owner = THIS_MODULE, @@ -1514,24 +1357,6 @@ static struct pstore_info efi_pstore_info = { .erase = efi_pstore_erase, }; -static void efivar_pstore_register(struct efivars *efivars) -{ - efivars->efi_pstore_info = efi_pstore_info; - efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); - if (efivars->efi_pstore_info.buf) { - efivars->efi_pstore_info.bufsize = 1024; - efivars->efi_pstore_info.data = efivars; - spin_lock_init(&efivars->efi_pstore_info.buf_lock); - pstore_register(&efivars->efi_pstore_info); - } -} -#else -static void efivar_pstore_register(struct efivars *efivars) -{ - return; -} -#endif - static ssize_t efivar_create(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) @@ -1552,7 +1377,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, return -EINVAL; } - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); /* * Does this variable already exist? @@ -1570,18 +1395,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, } } if (found) { - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return -EINVAL; } - status = check_var_size_locked(efivars, new_var->Attributes, - new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); - - if (status && status != EFI_UNSUPPORTED) { - spin_unlock_irq(&efivars->lock); - return efi_status_to_err(status); - } - /* now *really* create the variable via EFI */ status = efivars->ops->set_variable(new_var->VariableName, &new_var->VendorGuid, @@ -1592,10 +1409,10 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, if (status != EFI_SUCCESS) { printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", status); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return -EIO; } - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); /* Create the entry in sysfs. Locking is not required here */ status = efivar_create_sysfs_entry(efivars, @@ -1623,7 +1440,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); /* * Does this variable already exist? @@ -1641,7 +1458,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, } } if (!found) { - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return -EINVAL; } /* force the Attributes/DataSize to 0 to ensure deletion */ @@ -1657,65 +1474,18 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, if (status != EFI_SUCCESS) { printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", status); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return -EIO; } list_del(&search_efivar->list); /* We need to release this lock before unregistering. */ - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); efivar_unregister(search_efivar); /* It's dead Jim.... */ return count; } -static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) -{ - struct efivar_entry *entry, *n; - struct efivars *efivars = &__efivars; - unsigned long strsize1, strsize2; - bool found = false; - - strsize1 = utf16_strsize(variable_name, 1024); - list_for_each_entry_safe(entry, n, &efivars->list, list) { - strsize2 = utf16_strsize(entry->var.VariableName, 1024); - if (strsize1 == strsize2 && - !memcmp(variable_name, &(entry->var.VariableName), - strsize2) && - !efi_guidcmp(entry->var.VendorGuid, - *vendor)) { - found = true; - break; - } - } - return found; -} - -/* - * Returns the size of variable_name, in bytes, including the - * terminating NULL character, or variable_name_size if no NULL - * character is found among the first variable_name_size bytes. - */ -static unsigned long var_name_strnsize(efi_char16_t *variable_name, - unsigned long variable_name_size) -{ - unsigned long len; - efi_char16_t c; - - /* - * The variable name is, by definition, a NULL-terminated - * string, so make absolutely sure that variable_name_size is - * the value we expect it to be. If not, return the real size. - */ - for (len = 2; len <= variable_name_size; len += sizeof(c)) { - c = variable_name[(len / sizeof(c)) - 1]; - if (!c) - break; - } - - return min(len, variable_name_size); -} - /* * Let's not leave out systab information that snuck into * the efivars driver @@ -1824,9 +1594,9 @@ efivar_create_sysfs_entry(struct efivars *efivars, kfree(short_name); short_name = NULL; - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); list_add(&new_efivar->list, &efivars->list); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); return 0; } @@ -1895,9 +1665,9 @@ void unregister_efivars(struct efivars *efivars) struct efivar_entry *entry, *n; list_for_each_entry_safe(entry, n, &efivars->list, list) { - spin_lock_irq(&efivars->lock); + spin_lock(&efivars->lock); list_del(&entry->list); - spin_unlock_irq(&efivars->lock); + spin_unlock(&efivars->lock); efivar_unregister(entry); } if (efivars->new_var) @@ -1911,28 +1681,6 @@ void unregister_efivars(struct efivars *efivars) } EXPORT_SYMBOL_GPL(unregister_efivars); -/* - * Print a warning when duplicate EFI variables are encountered and - * disable the sysfs workqueue since the firmware is buggy. - */ -static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, - unsigned long len16) -{ - size_t i, len8 = len16 / sizeof(efi_char16_t); - char *s8; - - s8 = kzalloc(len8, GFP_KERNEL); - if (!s8) - return; - - for (i = 0; i < len8; i++) - s8[i] = s16[i]; - - printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", - s8, vendor_guid); - kfree(s8); -} - int register_efivars(struct efivars *efivars, const struct efivar_operations *ops, struct kobject *parent_kobj) @@ -1981,24 +1729,6 @@ int register_efivars(struct efivars *efivars, &vendor_guid); switch (status) { case EFI_SUCCESS: - variable_name_size = var_name_strnsize(variable_name, - variable_name_size); - - /* - * Some firmware implementations return the - * same variable name on multiple calls to - * get_next_variable(). Terminate the loop - * immediately as there is no guarantee that - * we'll ever see a different variable name, - * and may end up looping here forever. - */ - if (variable_is_present(variable_name, &vendor_guid)) { - dup_variable_bug(variable_name, &vendor_guid, - variable_name_size); - status = EFI_NOT_FOUND; - break; - } - efivar_create_sysfs_entry(efivars, variable_name_size, variable_name, @@ -2018,8 +1748,15 @@ int register_efivars(struct efivars *efivars, if (error) unregister_efivars(efivars); - if (!efivars_pstore_disable) - efivar_pstore_register(efivars); + efivars->efi_pstore_info = efi_pstore_info; + + efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL); + if (efivars->efi_pstore_info.buf) { + efivars->efi_pstore_info.bufsize = 1024; + efivars->efi_pstore_info.data = efivars; + spin_lock_init(&efivars->efi_pstore_info.buf_lock); + pstore_register(&efivars->efi_pstore_info); + } register_filesystem(&efivarfs_type); |