mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-04-13 09:59:31 +00:00
Merge branch 'efivarfs' into next
This commit is contained in:
commit
e7b4b1f61d
5 changed files with 179 additions and 232 deletions
|
@ -36,28 +36,41 @@ static ssize_t efivarfs_file_write(struct file *file,
|
||||||
if (IS_ERR(data))
|
if (IS_ERR(data))
|
||||||
return PTR_ERR(data);
|
return PTR_ERR(data);
|
||||||
|
|
||||||
|
inode_lock(inode);
|
||||||
|
if (var->removed) {
|
||||||
|
/*
|
||||||
|
* file got removed; don't allow a set. Caused by an
|
||||||
|
* unsuccessful create or successful delete write
|
||||||
|
* racing with us.
|
||||||
|
*/
|
||||||
|
bytes = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
bytes = efivar_entry_set_get_size(var, attributes, &datasize,
|
bytes = efivar_entry_set_get_size(var, attributes, &datasize,
|
||||||
data, &set);
|
data, &set);
|
||||||
if (!set && bytes) {
|
if (!set) {
|
||||||
if (bytes == -ENOENT)
|
if (bytes == -ENOENT)
|
||||||
bytes = -EIO;
|
bytes = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes == -ENOENT) {
|
if (bytes == -ENOENT) {
|
||||||
drop_nlink(inode);
|
/*
|
||||||
d_delete(file->f_path.dentry);
|
* zero size signals to release that the write deleted
|
||||||
dput(file->f_path.dentry);
|
* the variable
|
||||||
|
*/
|
||||||
|
i_size_write(inode, 0);
|
||||||
} else {
|
} else {
|
||||||
inode_lock(inode);
|
|
||||||
i_size_write(inode, datasize + sizeof(attributes));
|
i_size_write(inode, datasize + sizeof(attributes));
|
||||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||||
inode_unlock(inode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes = count;
|
bytes = count;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
kfree(data);
|
kfree(data);
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
|
@ -106,8 +119,36 @@ out_free:
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int efivarfs_file_release(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct efivar_entry *var = inode->i_private;
|
||||||
|
|
||||||
|
inode_lock(inode);
|
||||||
|
var->removed = (--var->open_count == 0 && i_size_read(inode) == 0);
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
|
if (var->removed)
|
||||||
|
simple_recursive_removal(file->f_path.dentry, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int efivarfs_file_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct efivar_entry *entry = inode->i_private;
|
||||||
|
|
||||||
|
file->private_data = entry;
|
||||||
|
|
||||||
|
inode_lock(inode);
|
||||||
|
entry->open_count++;
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const struct file_operations efivarfs_file_operations = {
|
const struct file_operations efivarfs_file_operations = {
|
||||||
.open = simple_open,
|
.open = efivarfs_file_open,
|
||||||
.read = efivarfs_file_read,
|
.read = efivarfs_file_read,
|
||||||
.write = efivarfs_file_write,
|
.write = efivarfs_file_write,
|
||||||
|
.release = efivarfs_file_release,
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,39 +77,34 @@ static bool efivarfs_valid_name(const char *str, int len)
|
||||||
static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
struct dentry *dentry, umode_t mode, bool excl)
|
struct dentry *dentry, umode_t mode, bool excl)
|
||||||
{
|
{
|
||||||
struct efivarfs_fs_info *info = dir->i_sb->s_fs_info;
|
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
struct efivar_entry *var;
|
struct efivar_entry *var;
|
||||||
int namelen, i = 0, err = 0;
|
int namelen, i = 0, err = 0;
|
||||||
bool is_removable = false;
|
bool is_removable = false;
|
||||||
|
efi_guid_t vendor;
|
||||||
|
|
||||||
if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
|
if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
|
|
||||||
if (!var)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* length of the variable name itself: remove GUID and separator */
|
/* length of the variable name itself: remove GUID and separator */
|
||||||
namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
|
namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
|
||||||
|
|
||||||
err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid);
|
err = guid_parse(dentry->d_name.name + namelen + 1, &vendor);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
return err;
|
||||||
if (guid_equal(&var->var.VendorGuid, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) {
|
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
|
||||||
err = -EPERM;
|
return -EPERM;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (efivar_variable_is_removable(var->var.VendorGuid,
|
if (efivar_variable_is_removable(vendor,
|
||||||
dentry->d_name.name, namelen))
|
dentry->d_name.name, namelen))
|
||||||
is_removable = true;
|
is_removable = true;
|
||||||
|
|
||||||
inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable);
|
inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0, is_removable);
|
||||||
if (!inode) {
|
if (!inode)
|
||||||
err = -ENOMEM;
|
return -ENOMEM;
|
||||||
goto out;
|
var = efivar_entry(inode);
|
||||||
}
|
|
||||||
|
var->var.VendorGuid = vendor;
|
||||||
|
|
||||||
for (i = 0; i < namelen; i++)
|
for (i = 0; i < namelen; i++)
|
||||||
var->var.VariableName[i] = dentry->d_name.name[i];
|
var->var.VariableName[i] = dentry->d_name.name[i];
|
||||||
|
@ -117,21 +112,11 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
var->var.VariableName[i] = '\0';
|
var->var.VariableName[i] = '\0';
|
||||||
|
|
||||||
inode->i_private = var;
|
inode->i_private = var;
|
||||||
kmemleak_ignore(var);
|
|
||||||
|
|
||||||
err = efivar_entry_add(var, &info->efivarfs_list);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
dget(dentry);
|
dget(dentry);
|
||||||
out:
|
|
||||||
if (err) {
|
return 0;
|
||||||
kfree(var);
|
|
||||||
if (inode)
|
|
||||||
iput(inode);
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
|
static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#ifndef EFIVAR_FS_INTERNAL_H
|
#ifndef EFIVAR_FS_INTERNAL_H
|
||||||
#define EFIVAR_FS_INTERNAL_H
|
#define EFIVAR_FS_INTERNAL_H
|
||||||
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
|
|
||||||
struct efivarfs_mount_opts {
|
struct efivarfs_mount_opts {
|
||||||
|
@ -16,7 +15,6 @@ struct efivarfs_mount_opts {
|
||||||
|
|
||||||
struct efivarfs_fs_info {
|
struct efivarfs_fs_info {
|
||||||
struct efivarfs_mount_opts mount_opts;
|
struct efivarfs_mount_opts mount_opts;
|
||||||
struct list_head efivarfs_list;
|
|
||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
struct notifier_block nb;
|
struct notifier_block nb;
|
||||||
};
|
};
|
||||||
|
@ -24,22 +22,23 @@ struct efivarfs_fs_info {
|
||||||
struct efi_variable {
|
struct efi_variable {
|
||||||
efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
|
efi_char16_t VariableName[EFI_VAR_NAME_LEN/sizeof(efi_char16_t)];
|
||||||
efi_guid_t VendorGuid;
|
efi_guid_t VendorGuid;
|
||||||
__u32 Attributes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct efivar_entry {
|
struct efivar_entry {
|
||||||
struct efi_variable var;
|
struct efi_variable var;
|
||||||
struct list_head list;
|
struct inode vfs_inode;
|
||||||
struct kobject kobj;
|
unsigned long open_count;
|
||||||
|
bool removed;
|
||||||
};
|
};
|
||||||
|
|
||||||
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
|
static inline struct efivar_entry *efivar_entry(struct inode *inode)
|
||||||
struct list_head *),
|
{
|
||||||
void *data, struct list_head *head);
|
return container_of(inode, struct efivar_entry, vfs_inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
||||||
|
void *data);
|
||||||
|
|
||||||
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
|
|
||||||
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
|
|
||||||
void efivar_entry_remove(struct efivar_entry *entry);
|
|
||||||
int efivar_entry_delete(struct efivar_entry *entry);
|
int efivar_entry_delete(struct efivar_entry *entry);
|
||||||
|
|
||||||
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);
|
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);
|
||||||
|
@ -50,13 +49,14 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
|
||||||
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
||||||
unsigned long *size, void *data, bool *set);
|
unsigned long *size, void *data, bool *set);
|
||||||
|
|
||||||
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
|
|
||||||
struct list_head *head, void *data);
|
|
||||||
|
|
||||||
bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
|
bool efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
|
||||||
unsigned long data_size);
|
unsigned long data_size);
|
||||||
bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
|
bool efivar_variable_is_removable(efi_guid_t vendor, const char *name,
|
||||||
size_t len);
|
size_t len);
|
||||||
|
char *efivar_get_utf8name(const efi_char16_t *name16, efi_guid_t *vendor);
|
||||||
|
bool efivarfs_variable_is_present(efi_char16_t *variable_name,
|
||||||
|
efi_guid_t *vendor, void *data);
|
||||||
|
|
||||||
extern const struct file_operations efivarfs_file_operations;
|
extern const struct file_operations efivarfs_file_operations;
|
||||||
extern const struct inode_operations efivarfs_dir_inode_operations;
|
extern const struct inode_operations efivarfs_dir_inode_operations;
|
||||||
|
|
|
@ -39,9 +39,24 @@ static int efivarfs_ops_notifier(struct notifier_block *nb, unsigned long event,
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void efivarfs_evict_inode(struct inode *inode)
|
static struct inode *efivarfs_alloc_inode(struct super_block *sb)
|
||||||
{
|
{
|
||||||
clear_inode(inode);
|
struct efivar_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (!entry)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
inode_init_once(&entry->vfs_inode);
|
||||||
|
entry->removed = false;
|
||||||
|
|
||||||
|
return &entry->vfs_inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void efivarfs_free_inode(struct inode *inode)
|
||||||
|
{
|
||||||
|
struct efivar_entry *entry = efivar_entry(inode);
|
||||||
|
|
||||||
|
kfree(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
|
static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
|
||||||
|
@ -106,7 +121,8 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
static const struct super_operations efivarfs_ops = {
|
static const struct super_operations efivarfs_ops = {
|
||||||
.statfs = efivarfs_statfs,
|
.statfs = efivarfs_statfs,
|
||||||
.drop_inode = generic_delete_inode,
|
.drop_inode = generic_delete_inode,
|
||||||
.evict_inode = efivarfs_evict_inode,
|
.alloc_inode = efivarfs_alloc_inode,
|
||||||
|
.free_inode = efivarfs_free_inode,
|
||||||
.show_options = efivarfs_show_options,
|
.show_options = efivarfs_show_options,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,9 +197,37 @@ static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool efivarfs_variable_is_present(efi_char16_t *variable_name,
|
||||||
|
efi_guid_t *vendor, void *data)
|
||||||
|
{
|
||||||
|
char *name = efivar_get_utf8name(variable_name, vendor);
|
||||||
|
struct super_block *sb = data;
|
||||||
|
struct dentry *dentry;
|
||||||
|
struct qstr qstr;
|
||||||
|
|
||||||
|
if (!name)
|
||||||
|
/*
|
||||||
|
* If the allocation failed there'll already be an
|
||||||
|
* error in the log (and likely a huge and growing
|
||||||
|
* number of them since they system will be under
|
||||||
|
* extreme memory pressure), so simply assume
|
||||||
|
* collision for safety but don't add to the log
|
||||||
|
* flood.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
|
||||||
|
qstr.name = name;
|
||||||
|
qstr.len = strlen(name);
|
||||||
|
dentry = d_hash_and_lookup(sb->s_root, &qstr);
|
||||||
|
kfree(name);
|
||||||
|
if (!IS_ERR_OR_NULL(dentry))
|
||||||
|
dput(dentry);
|
||||||
|
|
||||||
|
return dentry != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
|
static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
|
||||||
unsigned long name_size, void *data,
|
unsigned long name_size, void *data)
|
||||||
struct list_head *list)
|
|
||||||
{
|
{
|
||||||
struct super_block *sb = (struct super_block *)data;
|
struct super_block *sb = (struct super_block *)data;
|
||||||
struct efivar_entry *entry;
|
struct efivar_entry *entry;
|
||||||
|
@ -198,39 +242,26 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
|
||||||
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
|
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
name = efivar_get_utf8name(name16, &vendor);
|
||||||
if (!entry)
|
if (!name)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
memcpy(entry->var.VariableName, name16, name_size);
|
/* length of the variable name itself: remove GUID and separator */
|
||||||
memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
|
len = strlen(name) - EFI_VARIABLE_GUID_LEN - 1;
|
||||||
|
|
||||||
len = ucs2_utf8size(entry->var.VariableName);
|
if (efivar_variable_is_removable(vendor, name, len))
|
||||||
|
|
||||||
/* name, plus '-', plus GUID, plus NUL*/
|
|
||||||
name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
|
|
||||||
if (!name)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
ucs2_as_utf8(name, entry->var.VariableName, len);
|
|
||||||
|
|
||||||
if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
|
|
||||||
is_removable = true;
|
is_removable = true;
|
||||||
|
|
||||||
name[len] = '-';
|
|
||||||
|
|
||||||
efi_guid_to_str(&entry->var.VendorGuid, name + len + 1);
|
|
||||||
|
|
||||||
name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
|
|
||||||
|
|
||||||
/* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
|
|
||||||
strreplace(name, '/', '!');
|
|
||||||
|
|
||||||
inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
|
inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
|
||||||
is_removable);
|
is_removable);
|
||||||
if (!inode)
|
if (!inode)
|
||||||
goto fail_name;
|
goto fail_name;
|
||||||
|
|
||||||
|
entry = efivar_entry(inode);
|
||||||
|
|
||||||
|
memcpy(entry->var.VariableName, name16, name_size);
|
||||||
|
memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
|
||||||
|
|
||||||
dentry = efivarfs_alloc_dentry(root, name);
|
dentry = efivarfs_alloc_dentry(root, name);
|
||||||
if (IS_ERR(dentry)) {
|
if (IS_ERR(dentry)) {
|
||||||
err = PTR_ERR(dentry);
|
err = PTR_ERR(dentry);
|
||||||
|
@ -238,14 +269,13 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
|
||||||
}
|
}
|
||||||
|
|
||||||
__efivar_entry_get(entry, NULL, &size, NULL);
|
__efivar_entry_get(entry, NULL, &size, NULL);
|
||||||
__efivar_entry_add(entry, list);
|
|
||||||
|
|
||||||
/* copied by the above to local storage in the dentry. */
|
/* copied by the above to local storage in the dentry. */
|
||||||
kfree(name);
|
kfree(name);
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
inode->i_private = entry;
|
inode->i_private = entry;
|
||||||
i_size_write(inode, size + sizeof(entry->var.Attributes));
|
i_size_write(inode, size + sizeof(__u32)); /* attributes + data */
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
d_add(dentry, inode);
|
d_add(dentry, inode);
|
||||||
|
|
||||||
|
@ -255,16 +285,8 @@ fail_inode:
|
||||||
iput(inode);
|
iput(inode);
|
||||||
fail_name:
|
fail_name:
|
||||||
kfree(name);
|
kfree(name);
|
||||||
fail:
|
|
||||||
kfree(entry);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int efivarfs_destroy(struct efivar_entry *entry, void *data)
|
return err;
|
||||||
{
|
|
||||||
efivar_entry_remove(entry);
|
|
||||||
kfree(entry);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -336,7 +358,7 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return efivar_init(efivarfs_callback, sb, &sfi->efivarfs_list);
|
return efivar_init(efivarfs_callback, sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int efivarfs_get_tree(struct fs_context *fc)
|
static int efivarfs_get_tree(struct fs_context *fc)
|
||||||
|
@ -371,8 +393,6 @@ static int efivarfs_init_fs_context(struct fs_context *fc)
|
||||||
if (!sfi)
|
if (!sfi)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&sfi->efivarfs_list);
|
|
||||||
|
|
||||||
sfi->mount_opts.uid = GLOBAL_ROOT_UID;
|
sfi->mount_opts.uid = GLOBAL_ROOT_UID;
|
||||||
sfi->mount_opts.gid = GLOBAL_ROOT_GID;
|
sfi->mount_opts.gid = GLOBAL_ROOT_GID;
|
||||||
|
|
||||||
|
@ -388,8 +408,6 @@ static void efivarfs_kill_sb(struct super_block *sb)
|
||||||
blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb);
|
blocking_notifier_chain_unregister(&efivar_ops_nh, &sfi->nb);
|
||||||
kill_litter_super(sb);
|
kill_litter_super(sb);
|
||||||
|
|
||||||
/* Remove all entries and destroy */
|
|
||||||
efivar_entry_iter(efivarfs_destroy, &sfi->efivarfs_list, NULL);
|
|
||||||
kfree(sfi);
|
kfree(sfi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,6 +225,31 @@ variable_matches(const char *var_name, size_t len, const char *match_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
efivar_get_utf8name(const efi_char16_t *name16, efi_guid_t *vendor)
|
||||||
|
{
|
||||||
|
int len = ucs2_utf8size(name16);
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
/* name, plus '-', plus GUID, plus NUL*/
|
||||||
|
name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL);
|
||||||
|
if (!name)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ucs2_as_utf8(name, name16, len);
|
||||||
|
|
||||||
|
name[len] = '-';
|
||||||
|
|
||||||
|
efi_guid_to_str(vendor, name + len + 1);
|
||||||
|
|
||||||
|
name[len + EFI_VARIABLE_GUID_LEN+1] = '\0';
|
||||||
|
|
||||||
|
/* replace invalid slashes like kobject_set_name_vargs does for /sys/firmware/efi/vars. */
|
||||||
|
strreplace(name, '/', '!');
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
|
efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
|
||||||
unsigned long data_size)
|
unsigned long data_size)
|
||||||
|
@ -288,28 +313,6 @@ efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
struct efivar_entry *entry, *n;
|
|
||||||
unsigned long strsize1, strsize2;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
strsize1 = ucs2_strsize(variable_name, EFI_VAR_NAME_LEN);
|
|
||||||
list_for_each_entry_safe(entry, n, head, list) {
|
|
||||||
strsize2 = ucs2_strsize(entry->var.VariableName, EFI_VAR_NAME_LEN);
|
|
||||||
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
|
* Returns the size of variable_name, in bytes, including the
|
||||||
* terminating NULL character, or variable_name_size if no NULL
|
* terminating NULL character, or variable_name_size if no NULL
|
||||||
|
@ -361,16 +364,14 @@ static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid,
|
||||||
* efivar_init - build the initial list of EFI variables
|
* efivar_init - build the initial list of EFI variables
|
||||||
* @func: callback function to invoke for every variable
|
* @func: callback function to invoke for every variable
|
||||||
* @data: function-specific data to pass to @func
|
* @data: function-specific data to pass to @func
|
||||||
* @head: initialised head of variable list
|
|
||||||
*
|
*
|
||||||
* Get every EFI variable from the firmware and invoke @func. @func
|
* Get every EFI variable from the firmware and invoke @func. @func
|
||||||
* should call efivar_entry_add() to build the list of variables.
|
* should populate the initial dentry and inode tree.
|
||||||
*
|
*
|
||||||
* Returns 0 on success, or a kernel error code on failure.
|
* Returns 0 on success, or a kernel error code on failure.
|
||||||
*/
|
*/
|
||||||
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
|
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
||||||
struct list_head *),
|
void *data)
|
||||||
void *data, struct list_head *head)
|
|
||||||
{
|
{
|
||||||
unsigned long variable_name_size = 512;
|
unsigned long variable_name_size = 512;
|
||||||
efi_char16_t *variable_name;
|
efi_char16_t *variable_name;
|
||||||
|
@ -414,14 +415,14 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
|
||||||
* we'll ever see a different variable name,
|
* we'll ever see a different variable name,
|
||||||
* and may end up looping here forever.
|
* and may end up looping here forever.
|
||||||
*/
|
*/
|
||||||
if (variable_is_present(variable_name, &vendor_guid,
|
if (efivarfs_variable_is_present(variable_name,
|
||||||
head)) {
|
&vendor_guid, data)) {
|
||||||
dup_variable_bug(variable_name, &vendor_guid,
|
dup_variable_bug(variable_name, &vendor_guid,
|
||||||
variable_name_size);
|
variable_name_size);
|
||||||
status = EFI_NOT_FOUND;
|
status = EFI_NOT_FOUND;
|
||||||
} else {
|
} else {
|
||||||
err = func(variable_name, vendor_guid,
|
err = func(variable_name, vendor_guid,
|
||||||
variable_name_size, data, head);
|
variable_name_size, data);
|
||||||
if (err)
|
if (err)
|
||||||
status = EFI_NOT_FOUND;
|
status = EFI_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
@ -453,70 +454,12 @@ free:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* efivar_entry_add - add entry to variable list
|
* efivar_entry_delete - delete variable
|
||||||
* @entry: entry to add to list
|
|
||||||
* @head: list head
|
|
||||||
*
|
|
||||||
* Returns 0 on success, or a kernel error code on failure.
|
|
||||||
*/
|
|
||||||
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = efivar_lock();
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
list_add(&entry->list, head);
|
|
||||||
efivar_unlock();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __efivar_entry_add - add entry to variable list
|
|
||||||
* @entry: entry to add to list
|
|
||||||
* @head: list head
|
|
||||||
*/
|
|
||||||
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
|
|
||||||
{
|
|
||||||
list_add(&entry->list, head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* efivar_entry_remove - remove entry from variable list
|
|
||||||
* @entry: entry to remove from list
|
|
||||||
*
|
|
||||||
* Returns 0 on success, or a kernel error code on failure.
|
|
||||||
*/
|
|
||||||
void efivar_entry_remove(struct efivar_entry *entry)
|
|
||||||
{
|
|
||||||
list_del(&entry->list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* efivar_entry_list_del_unlock - remove entry from variable list
|
|
||||||
* @entry: entry to remove
|
|
||||||
*
|
|
||||||
* Remove @entry from the variable list and release the list lock.
|
|
||||||
*
|
|
||||||
* NOTE: slightly weird locking semantics here - we expect to be
|
|
||||||
* called with the efivars lock already held, and we release it before
|
|
||||||
* returning. This is because this function is usually called after
|
|
||||||
* set_variable() while the lock is still held.
|
|
||||||
*/
|
|
||||||
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
|
|
||||||
{
|
|
||||||
list_del(&entry->list);
|
|
||||||
efivar_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* efivar_entry_delete - delete variable and remove entry from list
|
|
||||||
* @entry: entry containing variable to delete
|
* @entry: entry containing variable to delete
|
||||||
*
|
*
|
||||||
* Delete the variable from the firmware and remove @entry from the
|
* Delete the variable from the firmware. It is the caller's
|
||||||
* variable list. It is the caller's responsibility to free @entry
|
* responsibility to free @entry (by deleting the dentry/inode) once
|
||||||
* once we return.
|
* we return.
|
||||||
*
|
*
|
||||||
* Returns 0 on success, -EINTR if we can't grab the semaphore,
|
* Returns 0 on success, -EINTR if we can't grab the semaphore,
|
||||||
* converted EFI status code if set_variable() fails.
|
* converted EFI status code if set_variable() fails.
|
||||||
|
@ -533,12 +476,10 @@ int efivar_entry_delete(struct efivar_entry *entry)
|
||||||
status = efivar_set_variable_locked(entry->var.VariableName,
|
status = efivar_set_variable_locked(entry->var.VariableName,
|
||||||
&entry->var.VendorGuid,
|
&entry->var.VendorGuid,
|
||||||
0, 0, NULL, false);
|
0, 0, NULL, false);
|
||||||
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
|
|
||||||
efivar_unlock();
|
efivar_unlock();
|
||||||
|
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND))
|
||||||
return efi_status_to_err(status);
|
return efi_status_to_err(status);
|
||||||
}
|
|
||||||
|
|
||||||
efivar_entry_list_del_unlock(entry);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,7 +573,7 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
|
||||||
* get_variable() fail.
|
* get_variable() fail.
|
||||||
*
|
*
|
||||||
* If the EFI variable does not exist when calling set_variable()
|
* If the EFI variable does not exist when calling set_variable()
|
||||||
* (EFI_NOT_FOUND), @entry is removed from the variable list.
|
* (EFI_NOT_FOUND).
|
||||||
*/
|
*/
|
||||||
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
||||||
unsigned long *size, void *data, bool *set)
|
unsigned long *size, void *data, bool *set)
|
||||||
|
@ -648,9 +589,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The lock here protects the get_variable call, the conditional
|
* The lock here protects the get_variable call and the
|
||||||
* set_variable call, and removal of the variable from the efivars
|
* conditional set_variable call
|
||||||
* list (in the case of an authenticated delete).
|
|
||||||
*/
|
*/
|
||||||
err = efivar_lock();
|
err = efivar_lock();
|
||||||
if (err)
|
if (err)
|
||||||
|
@ -676,9 +616,6 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
||||||
&entry->var.VendorGuid,
|
&entry->var.VendorGuid,
|
||||||
NULL, size, NULL);
|
NULL, size, NULL);
|
||||||
|
|
||||||
if (status == EFI_NOT_FOUND)
|
|
||||||
efivar_entry_list_del_unlock(entry);
|
|
||||||
else
|
|
||||||
efivar_unlock();
|
efivar_unlock();
|
||||||
|
|
||||||
if (status && status != EFI_BUFFER_TOO_SMALL)
|
if (status && status != EFI_BUFFER_TOO_SMALL)
|
||||||
|
@ -691,37 +628,3 @@ out:
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* efivar_entry_iter - iterate over variable list
|
|
||||||
* @func: callback function
|
|
||||||
* @head: head of variable list
|
|
||||||
* @data: function-specific data to pass to callback
|
|
||||||
*
|
|
||||||
* Iterate over the list of EFI variables and call @func with every
|
|
||||||
* entry on the list. It is safe for @func to remove entries in the
|
|
||||||
* list via efivar_entry_delete() while iterating.
|
|
||||||
*
|
|
||||||
* Some notes for the callback function:
|
|
||||||
* - a non-zero return value indicates an error and terminates the loop
|
|
||||||
* - @func is called from atomic context
|
|
||||||
*/
|
|
||||||
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
|
|
||||||
struct list_head *head, void *data)
|
|
||||||
{
|
|
||||||
struct efivar_entry *entry, *n;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
err = efivar_lock();
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
list_for_each_entry_safe(entry, n, head, list) {
|
|
||||||
err = func(entry, data);
|
|
||||||
if (err)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
efivar_unlock();
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue