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:
parent
4423d779af
commit
e14ab23dde
3 changed files with 1178 additions and 732 deletions
File diff suppressed because it is too large
Load diff
|
@ -882,12 +882,19 @@ static __init int gsmi_init(void)
|
|||
goto out_remove_bin_file;
|
||||
}
|
||||
|
||||
ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj);
|
||||
ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj);
|
||||
if (ret) {
|
||||
printk(KERN_INFO "gsmi: Failed to register efivars\n");
|
||||
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_die_notifier(&gsmi_die_notifier);
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
|
@ -919,7 +926,7 @@ static void __exit gsmi_exit(void)
|
|||
unregister_die_notifier(&gsmi_die_notifier);
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&gsmi_panic_notifier);
|
||||
unregister_efivars(&efivars);
|
||||
efivars_unregister(&efivars);
|
||||
|
||||
sysfs_remove_files(gsmi_kobj, gsmi_attrs);
|
||||
sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr);
|
||||
|
|
|
@ -662,6 +662,12 @@ static inline int efi_enabled(int facility)
|
|||
EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \
|
||||
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \
|
||||
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
|
||||
*/
|
||||
|
@ -762,19 +768,75 @@ struct efivars {
|
|||
* which is protected by the BKL, so that path is safe.
|
||||
*/
|
||||
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;
|
||||
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,
|
||||
struct kobject *parent_kobj);
|
||||
void unregister_efivars(struct efivars *efivars);
|
||||
struct kobject *kobject);
|
||||
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 */
|
||||
|
||||
|
|
Loading…
Reference in a new issue