efivars: efivar_entry API

There isn't really a formal interface for dealing with EFI variables
or struct efivar_entry. Historically, this has led to various bits of
code directly accessing the generic EFI variable ops, which inherently
ties it to specific EFI variable operations instead of indirectly
using whatever ops were registered with register_efivars(). This lead
to the efivarfs code only working with the generic EFI variable ops
and not CONFIG_GOOGLE_SMI.

Encapsulate everything that needs to access '__efivars' inside an
efivar_entry_* API and use the new API in the pstore, sysfs and
efivarfs code.

Much of the efivars code had to be rewritten to use this new API. For
instance, it is now up to the users of the API to build the initial
list of EFI variables in their efivar_init() callback function. The
variable list needs to be passed to efivar_init() which allows us to
keep work arounds for things like implementation bugs in
GetNextVariable() in a central location.

Allowing users of the API to use a callback function to build the list
greatly benefits the efivarfs code which needs to allocate inodes and
dentries for every variable.  It previously did this in a racy way
because the code ran without holding the variable spinlock. Both the
sysfs and efivarfs code maintain their own lists which means the two
interfaces can be running simultaneously without interference, though
it should be noted that because no synchronisation is performed it is
very easy to create inconsistencies. efibootmgr doesn't currently use
efivarfs and users are likely to also require the old sysfs interface,
so it makes sense to allow both to be built.

Reviewed-by: Tom Gundersen <teg@jklm.no>
Tested-by: Tom Gundersen <teg@jklm.no>
Cc: Seiji Aguchi <seiji.aguchi@hds.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Jeremy Kerr <jk@ozlabs.org>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Mike Waychison <mikew@google.com>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
This commit is contained in:
Matt Fleming 2013-02-03 20:16:40 +00:00
parent 4423d779af
commit e14ab23dde
3 changed files with 1178 additions and 732 deletions

File diff suppressed because it is too large Load Diff

View File

@ -882,12 +882,19 @@ static __init int gsmi_init(void)
goto out_remove_bin_file; goto out_remove_bin_file;
} }
ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj); ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj);
if (ret) { if (ret) {
printk(KERN_INFO "gsmi: Failed to register efivars\n"); printk(KERN_INFO "gsmi: Failed to register efivars\n");
goto out_remove_sysfs_files; goto out_remove_sysfs_files;
} }
ret = efivars_sysfs_init();
if (ret) {
printk(KERN_INFO "gsmi: Failed to create efivars files\n");
efivars_unregister(&efivars);
goto out_remove_sysfs_files;
}
register_reboot_notifier(&gsmi_reboot_notifier); register_reboot_notifier(&gsmi_reboot_notifier);
register_die_notifier(&gsmi_die_notifier); register_die_notifier(&gsmi_die_notifier);
atomic_notifier_chain_register(&panic_notifier_list, atomic_notifier_chain_register(&panic_notifier_list,
@ -919,7 +926,7 @@ static void __exit gsmi_exit(void)
unregister_die_notifier(&gsmi_die_notifier); unregister_die_notifier(&gsmi_die_notifier);
atomic_notifier_chain_unregister(&panic_notifier_list, atomic_notifier_chain_unregister(&panic_notifier_list,
&gsmi_panic_notifier); &gsmi_panic_notifier);
unregister_efivars(&efivars); efivars_unregister(&efivars);
sysfs_remove_files(gsmi_kobj, gsmi_attrs); sysfs_remove_files(gsmi_kobj, gsmi_attrs);
sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr);

View File

@ -662,6 +662,12 @@ static inline int efi_enabled(int facility)
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
EFI_VARIABLE_APPEND_WRITE) EFI_VARIABLE_APPEND_WRITE)
/*
* Length of a GUID string (strlen("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"))
* not including trailing NUL
*/
#define EFI_VARIABLE_GUID_LEN 36
/* /*
* The type of search to perform when calling boottime->locate_handle * The type of search to perform when calling boottime->locate_handle
*/ */
@ -762,19 +768,75 @@ struct efivars {
* which is protected by the BKL, so that path is safe. * which is protected by the BKL, so that path is safe.
*/ */
spinlock_t lock; spinlock_t lock;
struct list_head list;
struct kset *kset; struct kset *kset;
struct kobject *kobject; struct kobject *kobject;
struct bin_attribute *new_var, *del_var;
const struct efivar_operations *ops; const struct efivar_operations *ops;
struct efivar_entry *walk_entry;
struct pstore_info efi_pstore_info;
}; };
int register_efivars(struct efivars *efivars, /*
* The maximum size of VariableName + Data = 1024
* Therefore, it's reasonable to save that much
* space in each part of the structure,
* and we use a page for reading/writing.
*/
struct efi_variable {
efi_char16_t VariableName[1024/sizeof(efi_char16_t)];
efi_guid_t VendorGuid;
unsigned long DataSize;
__u8 Data[1024];
efi_status_t Status;
__u32 Attributes;
} __attribute__((packed));
struct efivar_entry {
struct efi_variable var;
struct list_head list;
struct kobject kobj;
};
int efivars_register(struct efivars *efivars,
const struct efivar_operations *ops, const struct efivar_operations *ops,
struct kobject *parent_kobj); struct kobject *kobject);
void unregister_efivars(struct efivars *efivars); int efivars_unregister(struct efivars *efivars);
struct kobject *efivars_kobject(void);
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
void *data, bool atomic, bool duplicates,
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);
int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
unsigned long *size, void *data);
int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
unsigned long size, void *data, struct list_head *head);
int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
unsigned long *size, void *data, bool *set);
int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
bool block, unsigned long size, void *data);
void efivar_entry_iter_begin(void);
void efivar_entry_iter_end(void);
int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data,
struct efivar_entry **prev);
int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data);
struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
struct list_head *head, bool remove);
bool efivar_validate(struct efi_variable *var, u8 *data, unsigned long len);
int efivars_sysfs_init(void);
#endif /* CONFIG_EFI_VARS */ #endif /* CONFIG_EFI_VARS */