efi/efivars: Expose RT service availability via efivars abstraction
authorArd Biesheuvel <ardb@kernel.org>
Wed, 8 Jul 2020 10:01:57 +0000 (13:01 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 29 Jul 2020 08:19:40 +0000 (10:19 +0200)
[ Upstream commit f88814cc2578c121e6edef686365036db72af0ed ]

Commit

  bf67fad19e493b ("efi: Use more granular check for availability for variable services")

introduced a check into the efivarfs, efi-pstore and other drivers that
aborts loading of the module if not all three variable runtime services
(GetVariable, SetVariable and GetNextVariable) are supported. However, this
results in efivarfs being unavailable entirely if only SetVariable support
is missing, which is only needed if you want to make any modifications.
Also, efi-pstore and the sysfs EFI variable interface could be backed by
another implementation of the 'efivars' abstraction, in which case it is
completely irrelevant which services are supported by the EFI firmware.

So make the generic 'efivars' abstraction dependent on the availibility of
the GetVariable and GetNextVariable EFI runtime services, and add a helper
'efivar_supports_writes()' to find out whether the currently active efivars
abstraction supports writes (and wire it up to the availability of
SetVariable for the generic one).

Then, use the efivar_supports_writes() helper to decide whether to permit
efivarfs to be mounted read-write, and whether to enable efi-pstore or the
sysfs EFI variable interface altogether.

Fixes: bf67fad19e493b ("efi: Use more granular check for availability for variable services")
Reported-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Acked-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Tested-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/firmware/efi/efi-pstore.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/efivars.c
drivers/firmware/efi/vars.c
fs/efivarfs/super.c
include/linux/efi.h

index c2f1d4e6630b10351a1cc428671d04ccc46a6b02..feb7fe6f2da76d7244c13d6ea5251f896c91fb8a 100644 (file)
@@ -356,10 +356,7 @@ static struct pstore_info efi_pstore_info = {
 
 static __init int efivars_pstore_init(void)
 {
-       if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
-               return 0;
-
-       if (!efivars_kobject())
+       if (!efivars_kobject() || !efivar_supports_writes())
                return 0;
 
        if (efivars_pstore_disable)
index 20a7ba47a79245306540681c8049a6e8a8d593d6..99446b3847265e71b1eef198bfe41d730be770b4 100644 (file)
@@ -176,11 +176,13 @@ static struct efivar_operations generic_ops;
 static int generic_ops_register(void)
 {
        generic_ops.get_variable = efi.get_variable;
-       generic_ops.set_variable = efi.set_variable;
-       generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
        generic_ops.get_next_variable = efi.get_next_variable;
        generic_ops.query_variable_store = efi_query_variable_store;
 
+       if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) {
+               generic_ops.set_variable = efi.set_variable;
+               generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
+       }
        return efivars_register(&generic_efivars, &generic_ops, efi_kobj);
 }
 
@@ -382,7 +384,8 @@ static int __init efisubsys_init(void)
                return -ENOMEM;
        }
 
-       if (efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES)) {
+       if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE |
+                                     EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME)) {
                efivar_ssdt_load();
                error = generic_ops_register();
                if (error)
@@ -416,7 +419,8 @@ static int __init efisubsys_init(void)
 err_remove_group:
        sysfs_remove_group(efi_kobj, &efi_subsys_attr_group);
 err_unregister:
-       if (efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
+       if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE |
+                                     EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME))
                generic_ops_unregister();
 err_put:
        kobject_put(efi_kobj);
index 26528a46d99e9e9f0db8c3f55dc3e4706bc774f4..dcea137142b3ceb3913024f6aafa363f4747dd3c 100644 (file)
@@ -680,11 +680,8 @@ int efivars_sysfs_init(void)
        struct kobject *parent_kobj = efivars_kobject();
        int error = 0;
 
-       if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
-               return -ENODEV;
-
        /* No efivars has been registered yet */
-       if (!parent_kobj)
+       if (!parent_kobj || !efivar_supports_writes())
                return 0;
 
        printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
index 5f2a4d162795c2ddfbcf122b9ad82963f3185db5..973eef234b365e530576fe09cc407cbb6e0d1366 100644 (file)
@@ -1229,3 +1229,9 @@ int efivars_unregister(struct efivars *efivars)
        return rv;
 }
 EXPORT_SYMBOL_GPL(efivars_unregister);
+
+int efivar_supports_writes(void)
+{
+       return __efivars && __efivars->ops->set_variable;
+}
+EXPORT_SYMBOL_GPL(efivar_supports_writes);
index 12c66f5d92dd2e5e1de7fc4d733bed11871adfa0..28bb5689333a59a69fa46bc6beba8e8d1f81acd2 100644 (file)
@@ -201,6 +201,9 @@ static int efivarfs_fill_super(struct super_block *sb, struct fs_context *fc)
        sb->s_d_op              = &efivarfs_d_ops;
        sb->s_time_gran         = 1;
 
+       if (!efivar_supports_writes())
+               sb->s_flags |= SB_RDONLY;
+
        inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0, true);
        if (!inode)
                return -ENOMEM;
@@ -252,9 +255,6 @@ static struct file_system_type efivarfs_type = {
 
 static __init int efivarfs_init(void)
 {
-       if (!efi_rt_services_supported(EFI_RT_SUPPORTED_VARIABLE_SERVICES))
-               return -ENODEV;
-
        if (!efivars_kobject())
                return -ENODEV;
 
index 9430d01c0c3d339f9402935d0d164223070c2602..650794abfa32674d20f50ecf279f50e7e9ce22f9 100644 (file)
@@ -991,6 +991,7 @@ int efivars_register(struct efivars *efivars,
 int efivars_unregister(struct efivars *efivars);
 struct kobject *efivars_kobject(void);
 
+int efivar_supports_writes(void);
 int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
                void *data, bool duplicates, struct list_head *head);