From 42a8af0fa4333701e0e318d3877f45bd6d51ce49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 Feb 2023 03:09:36 +0000 Subject: [PATCH 1/5] efi: x86: make kobj_type structure constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ee6d3dd4ed48 ("driver core: make kobj_type constant.") the driver core allows the usage of const struct kobj_type. Take advantage of this to constify the structure definition to prevent modification at runtime. Signed-off-by: Thomas Weißschuh Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/runtime-map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/platform/efi/runtime-map.c b/arch/x86/platform/efi/runtime-map.c index bbee682ef8cd1..a6f02cef3ca2b 100644 --- a/arch/x86/platform/efi/runtime-map.c +++ b/arch/x86/platform/efi/runtime-map.c @@ -93,7 +93,7 @@ static void map_release(struct kobject *kobj) kfree(entry); } -static struct kobj_type __refdata map_ktype = { +static const struct kobj_type __refconst map_ktype = { .sysfs_ops = &map_attr_ops, .default_groups = def_groups, .release = map_release, From 0153431c85af3d4470ac8c59a3f854a3926dff86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 27 Feb 2023 03:21:27 +0000 Subject: [PATCH 2/5] efi: make kobj_type structure constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ee6d3dd4ed48 ("driver core: make kobj_type constant.") the driver core allows the usage of const struct kobj_type. Take advantage of this to constify the structure definition to prevent modification at runtime. Signed-off-by: Thomas Weißschuh Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/esrt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index 87729c365be1a..c61398634d75f 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -156,7 +156,7 @@ static void esre_release(struct kobject *kobj) kfree(entry); } -static struct kobj_type esre1_ktype = { +static const struct kobj_type esre1_ktype = { .release = esre_release, .sysfs_ops = &esre_attr_ops, .default_groups = esre1_groups, From d86ff3333cb1d5f42d8898fb5fdb304e143c0237 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Wed, 17 May 2023 17:38:12 +0200 Subject: [PATCH 3/5] efivarfs: expose used and total size When writing EFI variables, one might get errors with no other message on why it fails. Being able to see how much is used by EFI variables helps analyzing such issues. Since this is not a conventional filesystem, block size is intentionally set to 1 instead of PAGE_SIZE. x86 quirks of reserved size are taken into account; so that available and free size can be different, further helping debugging space issues. With this patch, one can see the remaining space in EFI variable storage via efivarfs, like this: $ df -h /sys/firmware/efi/efivars/ Filesystem Size Used Avail Use% Mounted on efivarfs 176K 106K 66K 62% /sys/firmware/efi/efivars Signed-off-by: Anisse Astier [ardb: - rename efi_reserved_space() to efivar_reserved_space() - whitespace/coding style tweaks] Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/quirks.c | 8 +++++++ drivers/firmware/efi/efi.c | 1 + drivers/firmware/efi/vars.c | 12 +++++++++++ fs/efivarfs/super.c | 39 +++++++++++++++++++++++++++++++++- include/linux/efi.h | 11 ++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index b0b848d6933af..f0cc00032751d 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -114,6 +114,14 @@ void efi_delete_dummy_variable(void) EFI_VARIABLE_RUNTIME_ACCESS, 0, NULL); } +u64 efivar_reserved_space(void) +{ + if (efi_no_storage_paranoia) + return 0; + return EFI_MIN_RESERVE; +} +EXPORT_SYMBOL_GPL(efivar_reserved_space); + /* * In the nonblocking case we do not attempt to perform garbage * collection if we do not have enough free space. Rather, we do the diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index abeff7dc0b581..d0dfa007bffcc 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -211,6 +211,7 @@ static int generic_ops_register(void) generic_ops.get_variable = efi.get_variable; generic_ops.get_next_variable = efi.get_next_variable; generic_ops.query_variable_store = efi_query_variable_store; + generic_ops.query_variable_info = efi.query_variable_info; if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) { generic_ops.set_variable = efi.set_variable; diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index bfc5fa6aa47b6..e9dc7116daf13 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -245,3 +245,15 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor, return status; } EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR); + +efi_status_t efivar_query_variable_info(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + if (!__efivars->ops->query_variable_info) + return EFI_UNSUPPORTED; + return __efivars->ops->query_variable_info(attr, storage_space, + remaining_space, max_variable_size); +} +EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, EFIVAR); diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 482d612b716bb..e028fafa04f38 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "internal.h" @@ -23,8 +24,44 @@ static void efivarfs_evict_inode(struct inode *inode) clear_inode(inode); } +static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + const u32 attr = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + u64 storage_space, remaining_space, max_variable_size; + efi_status_t status; + + status = efivar_query_variable_info(attr, &storage_space, &remaining_space, + &max_variable_size); + if (status != EFI_SUCCESS) + return efi_status_to_err(status); + + /* + * This is not a normal filesystem, so no point in pretending it has a block + * size; we declare f_bsize to 1, so that we can then report the exact value + * sent by EFI QueryVariableInfo in f_blocks and f_bfree + */ + buf->f_bsize = 1; + buf->f_namelen = NAME_MAX; + buf->f_blocks = storage_space; + buf->f_bfree = remaining_space; + buf->f_type = dentry->d_sb->s_magic; + + /* + * In f_bavail we declare the free space that the kernel will allow writing + * when the storage_paranoia x86 quirk is active. To use more, users + * should boot the kernel with efi_no_storage_paranoia. + */ + if (remaining_space > efivar_reserved_space()) + buf->f_bavail = remaining_space - efivar_reserved_space(); + else + buf->f_bavail = 0; + + return 0; +} static const struct super_operations efivarfs_ops = { - .statfs = simple_statfs, + .statfs = efivarfs_statfs, .drop_inode = generic_delete_inode, .evict_inode = efivarfs_evict_inode, }; diff --git a/include/linux/efi.h b/include/linux/efi.h index 7aa62c92185f6..bed3c92cbc318 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1042,6 +1042,7 @@ struct efivar_operations { efi_set_variable_t *set_variable; efi_set_variable_t *set_variable_nonblocking; efi_query_variable_store_t *query_variable_store; + efi_query_variable_info_t *query_variable_info; }; struct efivars { @@ -1049,6 +1050,12 @@ struct efivars { const struct efivar_operations *ops; }; +#ifdef CONFIG_X86 +u64 __attribute_const__ efivar_reserved_space(void); +#else +static inline u64 efivar_reserved_space(void) { return 0; } +#endif + /* * The maximum size of VariableName + Data = 1024 * Therefore, it's reasonable to save that much @@ -1087,6 +1094,10 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor, efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor, u32 attr, unsigned long data_size, void *data); +efi_status_t efivar_query_variable_info(u32 attr, u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size); + #if IS_ENABLED(CONFIG_EFI_CAPSULE_LOADER) extern bool efi_capsule_pending(int *reset_type); From d0a1865cf7e2211d9227592ef4141f4632e33908 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Fri, 12 May 2023 19:43:02 +0000 Subject: [PATCH 4/5] efi/esrt: Allow ESRT access without CAP_SYS_ADMIN Access to the files in /sys/firmware/efi/esrt has been restricted to CAP_SYS_ADMIN since support for ESRT was added, but this seems overly restrictive given that the files are read-only and just provide information about UEFI firmware updates. Remove the CAP_SYS_ADMIN restriction so that a non-root process can read the files, provided a suitably-privileged process changes the file ownership first. The files are still read-only and still owned by root by default. Signed-off-by: Nicholas Bishop Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/esrt.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index c61398634d75f..7a81c0ce47805 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -95,10 +95,6 @@ static ssize_t esre_attr_show(struct kobject *kobj, struct esre_entry *entry = to_entry(kobj); struct esre_attribute *attr = to_attr(_attr); - /* Don't tell normal users what firmware versions we've got... */ - if (!capable(CAP_SYS_ADMIN)) - return -EACCES; - return attr->show(entry, buf); } From 2e28a798c3092ea42b968fa16ac835969d124898 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 27 Jun 2023 09:33:09 +0200 Subject: [PATCH 5/5] efi/libstub: Disable PCI DMA before grabbing the EFI memory map Currently, the EFI stub will disable PCI DMA as the very last thing it does before calling ExitBootServices(), to avoid interfering with the firmware's normal operation as much as possible. However, the stub will invoke DisconnectController() on all endpoints downstream of the PCI bridges it disables, and this may affect the layout of the EFI memory map, making it substantially more likely that ExitBootServices() will fail the first time around, and that the EFI memory map needs to be reloaded. This, in turn, increases the likelihood that the slack space we allocated is insufficient (and we can no longer allocate memory via boot services after having called ExitBootServices() once), causing the second call to GetMemoryMap (and therefore the boot) to fail. This makes the PCI DMA disable feature a bit more fragile than it already is, so let's make it more robust, by allocating the space for the EFI memory map after disabling PCI DMA. Fixes: 4444f8541dad16fe ("efi: Allow disabling PCI busmastering on bridges during boot") Reported-by: Glenn Washburn Acked-by: Matthew Garrett Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 1e0203d74691f..732984295295f 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -378,6 +378,9 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, struct efi_boot_memmap *map; efi_status_t status; + if (efi_disable_pci_dma) + efi_pci_disable_bridge_busmaster(); + status = efi_get_memory_map(&map, true); if (status != EFI_SUCCESS) return status; @@ -388,9 +391,6 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv, return status; } - if (efi_disable_pci_dma) - efi_pci_disable_bridge_busmaster(); - status = efi_bs_call(exit_boot_services, handle, map->map_key); if (status == EFI_INVALID_PARAMETER) {