diff --git a/data/bash-completion/fwupdtool.in b/data/bash-completion/fwupdtool.in index a3772618b..d401ba19a 100644 --- a/data/bash-completion/fwupdtool.in +++ b/data/bash-completion/fwupdtool.in @@ -53,6 +53,7 @@ _fwupdtool_opts=( '--prepare' '--cleanup' '--filter' + '--method' '--disable-ssl-strict' '--no-safety-check' '--ignore-checksum' diff --git a/libfwupdplugin/fu-efivar.h b/libfwupdplugin/fu-efivar.h index 93cfc543a..3886f5acd 100644 --- a/libfwupdplugin/fu-efivar.h +++ b/libfwupdplugin/fu-efivar.h @@ -14,6 +14,7 @@ #define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" #define FU_EFIVAR_GUID_SECURITY_DATABASE "d719b2cb-3d3a-4596-a3bc-dad00e67656f" #define FU_EFIVAR_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" +#define FU_EFIVAR_GUID_EFI_CAPSULE_REPORT "39b68c46-f7fb-441b-b6ec-16b0f69821f3" #define FU_EFIVAR_ATTR_NON_VOLATILE (1 << 0) #define FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS (1 << 1) diff --git a/plugins/uefi-capsule/fu-plugin-uefi-capsule.c b/plugins/uefi-capsule/fu-plugin-uefi-capsule.c index b0f6a836a..ac05ff826 100644 --- a/plugins/uefi-capsule/fu-plugin-uefi-capsule.c +++ b/plugins/uefi-capsule/fu-plugin-uefi-capsule.c @@ -8,14 +8,15 @@ #include "config.h" #include + #include #include #include "fu-uefi-backend.h" #include "fu-uefi-bgrt.h" #include "fu-uefi-bootmgr.h" +#include "fu-uefi-cod-device.h" #include "fu-uefi-common.h" -#include "fu-uefi-device.h" struct FuPluginData { FuUefiBgrt *bgrt; @@ -386,10 +387,11 @@ static void fu_plugin_uefi_capsule_register_proxy_device (FuPlugin *plugin, FuDevice *device) { FuPluginData *data = fu_plugin_get_data (plugin); - g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_dev (device); + g_autoptr(FuUefiDevice) dev = NULL; g_autoptr(GError) error_local = NULL; /* load all configuration variables */ + dev = fu_uefi_backend_device_new_from_dev(FU_UEFI_BACKEND(data->backend), device); fu_plugin_uefi_capsule_load_config (plugin, FU_DEVICE (dev)); if (data->esp == NULL) data->esp = fu_common_get_esp_default (&error_local); @@ -662,6 +664,36 @@ fu_plugin_uefi_update_state_notify_cb (GObject *object, } } +static gboolean +fu_backend_uefi_check_cod_support(GError **error) +{ + gsize bufsz = 0; + guint64 value = 0; + g_autofree guint8 *buf = NULL; + + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "OsIndicationsSupported", + &buf, + &bufsz, + NULL, + error)) { + g_prefix_error(error, "failed to read EFI variable: "); + return FALSE; + } + if (!fu_common_read_uint64_safe(buf, bufsz, 0x0, &value, G_LITTLE_ENDIAN, error)) + return FALSE; + if ((value & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) { + g_set_error_literal(error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Capsule-on-Disk is not supported"); + return FALSE; + } + + /* success */ + return TRUE; +} + gboolean fu_plugin_coldplug (FuPlugin *plugin, GError **error) { @@ -682,6 +714,17 @@ fu_plugin_coldplug (FuPlugin *plugin, GError **error) } } + /* firmware may lie */ + if (!fu_plugin_get_config_value_boolean(plugin, "DisableCapsuleUpdateOnDisk")) { + g_autoptr(GError) error_cod = NULL; + if (!fu_backend_uefi_check_cod_support(&error_cod)) { + g_debug("not using CapsuleOnDisk support: %s", error_cod->message); + } else { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(data->backend), + FU_TYPE_UEFI_COD_DEVICE); + } + } + /* add each device */ if (!fu_backend_coldplug (data->backend, error)) return FALSE; diff --git a/plugins/uefi-capsule/fu-self-test.c b/plugins/uefi-capsule/fu-self-test.c index 662baa460..cbd076a68 100644 --- a/plugins/uefi-capsule/fu-self-test.c +++ b/plugins/uefi-capsule/fu-self-test.c @@ -9,12 +9,11 @@ #include #include "fu-context-private.h" - #include "fu-ucs2.h" #include "fu-uefi-backend.h" #include "fu-uefi-bgrt.h" +#include "fu-uefi-cod-device.h" #include "fu-uefi-common.h" -#include "fu-uefi-device.h" #include "fu-uefi-pcrs.h" static void @@ -145,6 +144,103 @@ fu_uefi_bitmap_func (void) g_assert_cmpint (height, ==, 24); } +static GByteArray * +fu_uefi_cod_device_build_efi_string(const gchar *text) +{ + GByteArray *array = g_byte_array_new(); + glong items_written = 0; + g_autofree gunichar2 *test_utf16 = NULL; + g_autoptr(GError) error = NULL; + + fu_byte_array_append_uint32(array, 0x0, G_LITTLE_ENDIAN); /* attrs */ + test_utf16 = g_utf8_to_utf16(text, -1, NULL, &items_written, &error); + g_assert_no_error(error); + g_assert_nonnull(test_utf16); + g_byte_array_append(array, (const guint8 *)test_utf16, items_written * 2); + return array; +} + +static GByteArray * +fu_uefi_cod_device_build_efi_result(const gchar *guidstr) +{ + GByteArray *array = g_byte_array_new(); + fwupd_guid_t guid = {0x0}; + gboolean ret; + guint8 timestamp[16] = {0x0}; + g_autoptr(GError) error = NULL; + + fu_byte_array_append_uint32(array, 0x0, G_LITTLE_ENDIAN); /* attrs */ + fu_byte_array_append_uint32(array, 0x3A, G_LITTLE_ENDIAN); /* VariableTotalSize */ + fu_byte_array_append_uint32(array, 0xFF, G_LITTLE_ENDIAN); /* Reserved */ + ret = fwupd_guid_from_string(guidstr, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_byte_array_append(array, guid, sizeof(guid)); /* CapsuleGuid */ + g_byte_array_append(array, timestamp, sizeof(timestamp)); /* CapsuleProcessed */ + fu_byte_array_append_uint32(array, + FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT, + G_LITTLE_ENDIAN); /* Status */ + return array; +} + +static void +fu_uefi_cod_device_write_efi_name(const gchar *name, GByteArray *array) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + g_autofree gchar *fn = g_strdup_printf("%s-%s", name, FU_EFIVAR_GUID_EFI_CAPSULE_REPORT); + g_autofree gchar *path = g_build_filename(TESTDATADIR, "efi", "efivars", fn, NULL); + ret = g_file_set_contents(path, (gchar *)array->data, array->len, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_uefi_cod_device_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) dev = NULL; + g_autoptr(GError) error = NULL; + g_autofree gchar *str = NULL; + + /* these are checked into git and so are not required */ + if (g_getenv("FU_UEFI_CAPSULE_RECREATE_COD_SELF_TEST_DATA") != NULL) { + g_autoptr(GByteArray) cap0 = NULL; + g_autoptr(GByteArray) cap1 = NULL; + g_autoptr(GByteArray) last = NULL; + g_autoptr(GByteArray) max = NULL; + + last = fu_uefi_cod_device_build_efi_string("Capsule0001"); + max = fu_uefi_cod_device_build_efi_string("Capsule9999"); + cap0 = fu_uefi_cod_device_build_efi_result("99999999-bf9d-540b-b92b-172ce31013c1"); + cap1 = fu_uefi_cod_device_build_efi_result("cc4cbfa9-bf9d-540b-b92b-172ce31013c1"); + fu_uefi_cod_device_write_efi_name("CapsuleLast", last); + fu_uefi_cod_device_write_efi_name("CapsuleMax", max); + fu_uefi_cod_device_write_efi_name("Capsule0000", cap0); + fu_uefi_cod_device_write_efi_name("Capsule0001", cap1); + } + + /* create device */ + dev = g_object_new(FU_TYPE_UEFI_COD_DEVICE, + "fw-class", + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + NULL); + ret = fu_device_get_results(dev, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* debug */ + str = fu_device_to_string(dev); + g_debug("%s", str); + g_assert_cmpint(fu_device_get_update_state(dev), ==, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + g_assert_cmpstr(fu_device_get_update_error(dev), + ==, + "failed to update to 0: battery level is too low"); + g_assert_cmpint(fu_uefi_device_get_status(FU_UEFI_DEVICE(dev)), + ==, + FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT); +} + static void fu_uefi_device_func (void) { @@ -258,6 +354,7 @@ main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_setenv ("FWUPD_SYSFSFWDIR", TESTDATADIR, TRUE); g_setenv ("FWUPD_SYSFSDRIVERDIR", TESTDATADIR, TRUE); + g_setenv("FWUPD_UEFI_TEST", "1", TRUE); /* only critical and error are fatal */ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); @@ -271,6 +368,7 @@ main (int argc, char **argv) g_test_add_func ("/uefi/framebuffer", fu_uefi_framebuffer_func); g_test_add_func ("/uefi/bitmap", fu_uefi_bitmap_func); g_test_add_func ("/uefi/device", fu_uefi_device_func); + g_test_add_func("/uefi/cod-device", fu_uefi_cod_device_func); g_test_add_func ("/uefi/update-info", fu_uefi_update_info_func); g_test_add_func ("/uefi/plugin", fu_uefi_plugin_func); return g_test_run (); diff --git a/plugins/uefi-capsule/fu-uefi-backend-freebsd.c b/plugins/uefi-capsule/fu-uefi-backend-freebsd.c index 6a8142b70..c24bce5be 100644 --- a/plugins/uefi-capsule/fu-uefi-backend-freebsd.c +++ b/plugins/uefi-capsule/fu-uefi-backend-freebsd.c @@ -20,20 +20,23 @@ #include +#include "fu-uefi-backend-freebsd.h" #include "fu-uefi-common.h" #include "fu-uefi-device.h" -#include "fu-uefi-backend.h" -struct _FuUefiBackend { - FuBackend parent_instance; +struct _FuUefiBackendFreebsd { + FuUefiBackend parent_instance; }; -G_DEFINE_TYPE (FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND) +G_DEFINE_TYPE(FuUefiBackendFreebsd, fu_uefi_backend_freebsd, FU_TYPE_UEFI_BACKEND) #ifdef HAVE_FREEBSD_ESRT static FuUefiDevice * -fu_uefi_backend_device_new (struct efi_esrt_entry_v1 *entry, guint64 idx, GError **error) +fu_uefi_backend_device_new(FuUefiBackend *self, + struct efi_esrt_entry_v1 *entry, + guint64 idx, + GError **error) { g_autoptr(FuUefiDevice) dev = NULL; g_autofree gchar *fw_class = NULL; @@ -50,17 +53,26 @@ fu_uefi_backend_device_new (struct efi_esrt_entry_v1 *entry, guint64 idx, GError } /* create object */ - dev = g_object_new (FU_TYPE_UEFI_DEVICE, - "fw-class", fw_class, - "capsule-flags", entry->capsule_flags, - "kind", entry->fw_type, - "fw-version", entry->fw_version, - "last-attempt-status", entry->last_attempt_status, - "last-attempt-version", entry->last_attempt_version, - "fw-version-lowest", entry->lowest_supported_fw_version, - "fmp-hardware-instance", (guint64) 0x0, - "version-format", FWUPD_VERSION_FORMAT_NUMBER, - NULL); + dev = g_object_new(fu_uefi_backend_get_device_gtype(self), + "fw-class", + fw_class, + "capsule-flags", + entry->capsule_flags, + "kind", + entry->fw_type, + "fw-version", + entry->fw_version, + "last-attempt-status", + entry->last_attempt_status, + "last-attempt-version", + entry->last_attempt_version, + "fw-version-lowest", + entry->lowest_supported_fw_version, + "fmp-hardware-instance", + (guint64)0x0, + "version-format", + FWUPD_VERSION_FORMAT_NUMBER, + NULL); /* set ID */ phys_id = g_strdup_printf ("ESRT/%u", (guint)idx); @@ -71,7 +83,7 @@ fu_uefi_backend_device_new (struct efi_esrt_entry_v1 *entry, guint64 idx, GError #endif static gboolean -fu_uefi_backend_setup (FuBackend *backend, GError **error) +fu_uefi_backend_freebsd_setup(FuBackend *backend, GError **error) { g_autofree gchar *efi_ver = fu_kenv_get_string ("efi-version", error); if (efi_ver == NULL) { @@ -90,9 +102,10 @@ fu_uefi_backend_setup (FuBackend *backend, GError **error) } static gboolean -fu_uefi_backend_coldplug (FuBackend *backend, GError **error) +fu_uefi_backend_freebsd_coldplug(FuBackend *backend, GError **error) { #ifdef HAVE_FREEBSD_ESRT + FuUefiBackend *self = FU_UEFI_BACKEND(backend); struct efi_get_table_ioc table = { .uuid = EFI_TABLE_ESRT }; @@ -141,9 +154,8 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error) entries = (struct efi_esrt_entry_v1 *)esrt->entries; for (guint i = 0; i < esrt->fw_resource_count; i++) { - g_autoptr(FuUefiDevice) dev = fu_uefi_backend_device_new (&entries[i], - i, - error); + g_autoptr(FuUefiDevice) dev = NULL; + dev = fu_uefi_backend_device_new(self, &entries[i], i, error); if (dev == NULL) return FALSE; @@ -161,24 +173,26 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error) #endif } -static void -fu_uefi_backend_init (FuUefiBackend *self) +void +fu_uefi_backend_freebsd_set_device_gtype(FuBackend *backend, GType device_gtype) { } static void -fu_uefi_backend_class_init (FuUefiBackendClass *klass) +fu_uefi_backend_freebsd_init(FuUefiBackendFreebsd *self) +{ +} + +static void +fu_uefi_backend_freebsd_class_init(FuUefiBackendFreebsdClass *klass) { FuBackendClass *klass_backend = FU_BACKEND_CLASS (klass); - klass_backend->setup = fu_uefi_backend_setup; - klass_backend->coldplug = fu_uefi_backend_coldplug; + klass_backend->setup = fu_uefi_backend_freebsd_setup; + klass_backend->coldplug = fu_uefi_backend_freebsd_coldplug; } FuBackend * fu_uefi_backend_new (FuContext *ctx) { - return FU_BACKEND (g_object_new (FU_TYPE_UEFI_BACKEND, - "name", "uefi", - "context", ctx, - NULL)); + return g_object_new(FU_TYPE_UEFI_BACKEND_FREEBSD, "name", "uefi", "context", ctx, NULL); } diff --git a/plugins/uefi-capsule/fu-uefi-backend-freebsd.h b/plugins/uefi-capsule/fu-uefi-backend-freebsd.h new file mode 100644 index 000000000..a70a20a6a --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-backend-freebsd.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-backend.h" + +#define FU_TYPE_UEFI_BACKEND_FREEBSD (fu_uefi_backend_freebsd_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiBackendFreebsd, + fu_uefi_backend_freebsd, + FU, + UEFI_BACKEND_FREEBSD, + FuUefiBackend) diff --git a/plugins/uefi-capsule/fu-uefi-backend-linux.c b/plugins/uefi-capsule/fu-uefi-backend-linux.c index 383cd64fc..920008f6f 100644 --- a/plugins/uefi-capsule/fu-uefi-backend-linux.c +++ b/plugins/uefi-capsule/fu-uefi-backend-linux.c @@ -7,11 +7,13 @@ #include "config.h" #include + #include +#include "fu-uefi-backend-linux.h" +#include "fu-uefi-cod-device.h" #include "fu-uefi-common.h" -#include "fu-uefi-device.h" -#include "fu-uefi-backend.h" +#include "fu-uefi-nvram-device.h" #ifndef HAVE_GIO_2_55_0 #pragma clang diagnostic push @@ -20,21 +22,22 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUnixMountEntry, g_unix_mount_free) #pragma clang diagnostic pop #endif -struct _FuUefiBackend { - FuBackend parent_instance; +struct _FuUefiBackendLinux { + FuUefiBackend parent_instance; + gboolean use_rt_set_variable; }; -G_DEFINE_TYPE (FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND) +G_DEFINE_TYPE(FuUefiBackendLinux, fu_uefi_backend_linux, FU_TYPE_UEFI_BACKEND) /* yes, unsized uint_t */ static guint -fu_uefi_backend_read (const gchar *path, const gchar *filename) +fu_uefi_backend_linux_read(const gchar *path, const gchar *filename) { return fu_uefi_read_file_as_uint64 (path, filename); } static FuUefiDevice * -fu_uefi_backend_device_new (const gchar *path) +fu_uefi_backend_linux_device_new(FuUefiBackendLinux *self, const gchar *path) { g_autoptr(FuUefiDevice) dev = NULL; g_autofree gchar *fw_class = NULL; @@ -53,17 +56,30 @@ fu_uefi_backend_device_new (const gchar *path) * The hardware instance is not in the ESRT table and we should really * write the EFI stub to query with FMP -- but we still have not ever * seen a PCIe device with FMP support... */ - dev = g_object_new (FU_TYPE_UEFI_DEVICE, - "fw-class", fw_class, - "capsule-flags", fu_uefi_backend_read (path, "capsule_flags"), - "kind", fu_uefi_backend_read (path, "fw_type"), - "fw-version", fu_uefi_backend_read (path, "fw_version"), - "last-attempt-status", fu_uefi_backend_read (path, "last_attempt_status"), - "last-attempt-version", fu_uefi_backend_read (path, "last_attempt_version"), - "fw-version-lowest", fu_uefi_backend_read (path, "lowest_supported_fw_version"), - "fmp-hardware-instance", (guint64) 0x0, - "version-format", FWUPD_VERSION_FORMAT_NUMBER, - NULL); + dev = g_object_new(fu_uefi_backend_get_device_gtype(FU_UEFI_BACKEND(self)), + "fw-class", + fw_class, + "capsule-flags", + fu_uefi_backend_linux_read(path, "capsule_flags"), + "kind", + fu_uefi_backend_linux_read(path, "fw_type"), + "fw-version", + fu_uefi_backend_linux_read(path, "fw_version"), + "last-attempt-status", + fu_uefi_backend_linux_read(path, "last_attempt_status"), + "last-attempt-version", + fu_uefi_backend_linux_read(path, "last_attempt_version"), + "fw-version-lowest", + fu_uefi_backend_linux_read(path, "lowest_supported_fw_version"), + "fmp-hardware-instance", + (guint64)0x0, + "version-format", + FWUPD_VERSION_FORMAT_NUMBER, + NULL); + + /* u-boot for instance */ + if (!self->use_rt_set_variable) + fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE); /* set ID */ fu_device_set_physical_id (FU_DEVICE (dev), path); @@ -71,14 +87,56 @@ fu_uefi_backend_device_new (const gchar *path) } static gboolean -fu_uefi_backend_coldplug (FuBackend *backend, GError **error) +fu_uefi_backend_linux_check_efivarfs(FuUefiBackendLinux *self, GError **error) { + g_autofree gchar *sysfsfwdir = fu_common_get_path(FU_PATH_KIND_SYSFSDIR_FW); + g_autofree gchar *sysfsefivardir = g_build_filename(sysfsfwdir, "efi", "efivars", NULL); + g_autoptr(GUnixMountEntry) mount = g_unix_mount_at(sysfsefivardir, NULL); + + /* in the self tests */ + if (g_getenv("FWUPD_UEFI_TEST") != NULL) + return TRUE; + + if (mount == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "%s was not mounted", + sysfsefivardir); + return FALSE; + } + if (g_unix_mount_is_readonly(mount)) { + GType gtype = fu_uefi_backend_get_device_gtype(FU_UEFI_BACKEND(self)); + if (gtype != FU_TYPE_UEFI_COD_DEVICE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "%s is read only and no CoD", + sysfsefivardir); + return FALSE; + } + + /* this is fine! just do not use SetVariable... */ + self->use_rt_set_variable = FALSE; + } + + return TRUE; +} + +static gboolean +fu_uefi_backend_linux_coldplug(FuBackend *backend, GError **error) +{ + FuUefiBackendLinux *self = FU_UEFI_BACKEND_LINUX(backend); const gchar *fn; g_autofree gchar *esrt_entries = NULL; g_autofree gchar *esrt_path = NULL; g_autofree gchar *sysfsfwdir = NULL; g_autoptr(GDir) dir = NULL; + /* make sure that efivarfs is suitable */ + if (!fu_uefi_backend_linux_check_efivarfs(self, error)) + return FALSE; + /* get the directory of ESRT entries */ sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); esrt_path = g_build_filename (sysfsfwdir, "efi", "esrt", NULL); @@ -90,7 +148,7 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error) /* add each device */ while ((fn = g_dir_read_name (dir)) != NULL) { g_autofree gchar *path = g_build_filename (esrt_entries, fn, NULL); - g_autoptr(FuUefiDevice) dev = fu_uefi_backend_device_new (path); + g_autoptr(FuUefiDevice) dev = fu_uefi_backend_linux_device_new(self, path); fu_backend_device_added (backend, FU_DEVICE (dev)); } @@ -99,7 +157,7 @@ fu_uefi_backend_coldplug (FuBackend *backend, GError **error) } static gboolean -fu_uefi_backend_check_smbios_enabled (FuContext *ctx, GError **error) +fu_uefi_backend_linux_check_smbios_enabled(FuContext *ctx, GError **error) { const guint8 *data; gsize sz; @@ -140,32 +198,7 @@ fu_uefi_backend_check_smbios_enabled (FuContext *ctx, GError **error) } static gboolean -fu_uefi_backend_check_efivarfs_rw (GError **error) -{ - g_autofree gchar *sysfsfwdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); - g_autofree gchar *sysfsefivardir = g_build_filename (sysfsfwdir, "efi", "efivars", NULL); - g_autoptr(GUnixMountEntry) mount = g_unix_mount_at (sysfsefivardir, NULL); - - if (mount == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "%s was not mounted", sysfsefivardir); - return FALSE; - } - if (g_unix_mount_is_readonly (mount)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "%s is read only", sysfsefivardir); - return FALSE; - } - - return TRUE; -} - -static gboolean -fu_uefi_backend_setup (FuBackend *backend, GError **error) +fu_uefi_backend_linux_setup(FuBackend *backend, GError **error) { g_autoptr(GError) error_local = NULL; @@ -173,12 +206,9 @@ fu_uefi_backend_setup (FuBackend *backend, GError **error) if (g_getenv ("FWUPD_SYSFSFWDIR") != NULL) return TRUE; - /* make sure that efivarfs is rw */ - if (!fu_uefi_backend_check_efivarfs_rw (error)) - return FALSE; - /* check SMBIOS for 'UEFI Specification is supported' */ - if (!fu_uefi_backend_check_smbios_enabled (fu_backend_get_context (backend), &error_local)) { + if (!fu_uefi_backend_linux_check_smbios_enabled(fu_backend_get_context(backend), + &error_local)) { g_autofree gchar *fw = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_FW); g_autofree gchar *fn = g_build_filename (fw, "efi", NULL); if (g_file_test (fn, G_FILE_TEST_EXISTS)) { @@ -194,23 +224,21 @@ fu_uefi_backend_setup (FuBackend *backend, GError **error) } static void -fu_uefi_backend_init (FuUefiBackend *self) +fu_uefi_backend_linux_init(FuUefiBackendLinux *self) { + self->use_rt_set_variable = TRUE; } static void -fu_uefi_backend_class_init (FuUefiBackendClass *klass) +fu_uefi_backend_linux_class_init(FuUefiBackendLinuxClass *klass) { FuBackendClass *klass_backend = FU_BACKEND_CLASS (klass); - klass_backend->coldplug = fu_uefi_backend_coldplug; - klass_backend->setup = fu_uefi_backend_setup; + klass_backend->coldplug = fu_uefi_backend_linux_coldplug; + klass_backend->setup = fu_uefi_backend_linux_setup; } FuBackend * fu_uefi_backend_new (FuContext *ctx) { - return FU_BACKEND (g_object_new (FU_TYPE_UEFI_BACKEND, - "name", "uefi", - "context", ctx, - NULL)); + return g_object_new(FU_TYPE_UEFI_BACKEND_LINUX, "name", "uefi", "context", ctx, NULL); } diff --git a/plugins/uefi-capsule/fu-uefi-backend-linux.h b/plugins/uefi-capsule/fu-uefi-backend-linux.h new file mode 100644 index 000000000..72524ab0a --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-backend-linux.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-backend.h" + +#define FU_TYPE_UEFI_BACKEND_LINUX (fu_uefi_backend_linux_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiBackendLinux, + fu_uefi_backend_linux, + FU, + UEFI_BACKEND_LINUX, + FuUefiBackend) diff --git a/plugins/uefi-capsule/fu-uefi-backend.c b/plugins/uefi-capsule/fu-uefi-backend.c new file mode 100644 index 000000000..edfca13ae --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-backend.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-uefi-backend.h" +#include "fu-uefi-nvram-device.h" + +typedef struct { + GType device_gtype; +} FuUefiBackendPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuUefiBackend, fu_uefi_backend, FU_TYPE_BACKEND) + +#define GET_PRIVATE(o) (fu_uefi_backend_get_instance_private(o)) + +void +fu_uefi_backend_set_device_gtype(FuUefiBackend *self, GType device_gtype) +{ + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + priv->device_gtype = device_gtype; +} + +GType +fu_uefi_backend_get_device_gtype(FuUefiBackend *self) +{ + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + return priv->device_gtype; +} + +/* create virtual object not backed by an ESRT entry */ +FuUefiDevice * +fu_uefi_backend_device_new_from_dev(FuUefiBackend *self, FuDevice *dev) +{ + FuUefiDevice *device; + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + g_return_val_if_fail(fu_device_get_guid_default(dev) != NULL, NULL); + + tmp = fu_device_get_metadata(dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND); + device = + g_object_new(priv->device_gtype, + "fw-class", + fu_device_get_guid_default(dev), + "kind", + fu_uefi_device_kind_from_string(tmp), + "capsule-flags", + fu_device_get_metadata_integer(dev, FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS), + "fw-version", + fu_device_get_metadata_integer(dev, FU_DEVICE_METADATA_UEFI_FW_VERSION), + NULL); + fu_device_incorporate(FU_DEVICE(device), dev); + return device; +} + +FuUefiDevice * +fu_uefi_backend_device_new_from_guid(FuUefiBackend *self, const gchar *guid) +{ + FuUefiDevice *device; + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(guid != NULL, NULL); + + device = g_object_new(priv->device_gtype, "fw-class", guid, NULL); + fu_device_set_version_format(FU_DEVICE(device), FWUPD_VERSION_FORMAT_NUMBER); + return device; +} + +static void +fu_uefi_backend_init(FuUefiBackend *self) +{ + FuUefiBackendPrivate *priv = GET_PRIVATE(self); + priv->device_gtype = FU_TYPE_UEFI_NVRAM_DEVICE; +} + +static void +fu_uefi_backend_class_init(FuUefiBackendClass *klass) +{ +} diff --git a/plugins/uefi-capsule/fu-uefi-backend.h b/plugins/uefi-capsule/fu-uefi-backend.h index d5a3821dc..d590ee5f4 100644 --- a/plugins/uefi-capsule/fu-uefi-backend.h +++ b/plugins/uefi-capsule/fu-uefi-backend.h @@ -8,7 +8,22 @@ #include +#include "fu-uefi-device.h" + #define FU_TYPE_UEFI_BACKEND (fu_uefi_backend_get_type ()) -G_DECLARE_FINAL_TYPE (FuUefiBackend, fu_uefi_backend, FU, UEFI_BACKEND, FuBackend) +G_DECLARE_DERIVABLE_TYPE(FuUefiBackend, fu_uefi_backend, FU, UEFI_BACKEND, FuBackend) + +struct _FuUefiBackendClass { + FuBackendClass parent_class; +}; FuBackend *fu_uefi_backend_new (FuContext *ctx); +void +fu_uefi_backend_set_device_gtype(FuUefiBackend *self, GType device_gtype); +GType +fu_uefi_backend_get_device_gtype(FuUefiBackend *self); + +FuUefiDevice * +fu_uefi_backend_device_new_from_guid(FuUefiBackend *self, const gchar *guid); +FuUefiDevice * +fu_uefi_backend_device_new_from_dev(FuUefiBackend *self, FuDevice *dev); diff --git a/plugins/uefi-capsule/fu-uefi-bootmgr.c b/plugins/uefi-capsule/fu-uefi-bootmgr.c index 17fc6391d..e9d9f54ff 100644 --- a/plugins/uefi-capsule/fu-uefi-bootmgr.c +++ b/plugins/uefi-capsule/fu-uefi-bootmgr.c @@ -248,7 +248,7 @@ fu_uefi_setup_bootnext_with_dp (const guint8 *dp_buf, guint8 *opt, gssize opt_si } static gboolean -fu_uefi_cmp_asset (const gchar *source, const gchar *target) +fu_uefi_cmp_asset(const gchar *source, const gchar *target) { gsize len = 0; g_autofree gchar *source_checksum = NULL; @@ -257,36 +257,35 @@ fu_uefi_cmp_asset (const gchar *source, const gchar *target) g_autofree gchar *target_data = NULL; /* nothing in target yet */ - if (!g_file_test (target, G_FILE_TEST_EXISTS)) + if (!g_file_test(target, G_FILE_TEST_EXISTS)) return FALSE; /* test if the file needs to be updated */ - if (!g_file_get_contents (source, &source_data, &len, NULL)) + if (!g_file_get_contents(source, &source_data, &len, NULL)) return FALSE; - source_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - (guchar *) source_data, len); - if (!g_file_get_contents (target, &target_data, &len, NULL)) + source_checksum = + g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar *)source_data, len); + if (!g_file_get_contents(target, &target_data, &len, NULL)) return FALSE; - target_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - (guchar *) target_data, len); - return g_strcmp0 (target_checksum, source_checksum) == 0; + target_checksum = + g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar *)target_data, len); + return g_strcmp0(target_checksum, source_checksum) == 0; } static gboolean -fu_uefi_copy_asset (const gchar *source, const gchar *target, GError **error) +fu_uefi_copy_asset(const gchar *source, const gchar *target, GError **error) { - g_autoptr(GFile) source_file = g_file_new_for_path (source); - g_autoptr(GFile) target_file = g_file_new_for_path (target); + g_autoptr(GFile) source_file = g_file_new_for_path(source); + g_autoptr(GFile) target_file = g_file_new_for_path(target); - if (!g_file_copy (source_file, - target_file, - G_FILE_COPY_OVERWRITE, - NULL, - NULL, - NULL, - error)) { - g_prefix_error (error, "Failed to copy %s to %s: ", - source, target); + if (!g_file_copy(source_file, + target_file, + G_FILE_COPY_OVERWRITE, + NULL, + NULL, + NULL, + error)) { + g_prefix_error(error, "Failed to copy %s to %s: ", source, target); return FALSE; } diff --git a/plugins/uefi-capsule/fu-uefi-cod-device.c b/plugins/uefi-capsule/fu-uefi-cod-device.c new file mode 100644 index 000000000..8afd49167 --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-cod-device.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include + +#include "fu-uefi-cod-device.h" +#include "fu-uefi-common.h" + +struct _FuUefiCodDevice { + FuUefiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU_TYPE_UEFI_DEVICE) + +static gboolean +fu_uefi_cod_device_get_results_for_idx(FuDevice *device, guint idx, GError **error) +{ + FuUefiDevice *device_uefi = FU_UEFI_DEVICE(device); + fwupd_guid_t guid = {0x0}; + gsize bufsz = 0; + guint32 status = 0; + guint32 total_size = 0; + g_autofree gchar *guidstr = NULL; + g_autofree gchar *name = NULL; + g_autofree guint8 *buf = NULL; + + /* read out result */ + name = g_strdup_printf("Capsule%04u", idx); + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_CAPSULE_REPORT, + name, + &buf, + &bufsz, + NULL, + error)) { + g_prefix_error(error, "failed to read %s: ", name); + return FALSE; + } + + /* sanity check */ + if (!fu_common_read_uint32_safe(buf, bufsz, 0x00, &total_size, G_LITTLE_ENDIAN, error)) + return FALSE; + if (total_size < 0x3A) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "EFI_CAPSULE_RESULT_VARIABLE_HEADER too small"); + return FALSE; + } + + /* verify guid */ + if (!fu_memcpy_safe(guid, + sizeof(guid), + 0x0, /* dst */ + buf, + bufsz, + 0x08, /* src */ + sizeof(guid), + error)) + return FALSE; + guidstr = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + if (g_strcmp0(guidstr, fu_uefi_device_get_guid(device_uefi)) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "wrong GUID, expected %s, got %s", + fu_uefi_device_get_guid(device_uefi), + guidstr); + return FALSE; + } + + /* get status */ + if (!fu_common_read_uint32_safe(buf, bufsz, 0x28, &status, G_LITTLE_ENDIAN, error)) + return FALSE; + fu_uefi_device_set_status(device_uefi, status); + return TRUE; +} + +#define VARIABLE_IDX_SIZE 11 /* of CHAR16 */ + +static gboolean +fu_uefi_cod_device_get_variable_idx(const gchar *name, guint *value, GError **error) +{ + gsize bufsz = 0; + g_autofree guint8 *buf = NULL; + g_autofree gchar *str = NULL; + g_autoptr(GError) error_local = NULL; + gunichar2 buf16[VARIABLE_IDX_SIZE] = {0x0}; + + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_CAPSULE_REPORT, name, &buf, &bufsz, NULL, error)) + return FALSE; + if (!fu_memcpy_safe((guint8 *)buf16, + sizeof(buf16), + 0x0, /* dst */ + buf, + bufsz, + 0x0, /* src */ + sizeof(buf16), + error)) + return FALSE; + + /* parse the value */ + str = g_utf16_to_utf8(buf16, VARIABLE_IDX_SIZE, NULL, NULL, error); + if (str == NULL) + return FALSE; + if (!g_str_has_prefix(str, "Capsule")) { + g_set_error(error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "wrong contents, got %s", + str); + return FALSE; + } + if (value != NULL) + *value = fu_common_strtoull(str + strlen("Capsule")); + return TRUE; +} + +static gboolean +fu_uefi_cod_device_get_results(FuDevice *device, GError **error) +{ + guint capsule_last = 1024; + + /* tell us where to stop */ + if (!fu_uefi_cod_device_get_variable_idx("CapsuleLast", &capsule_last, error)) + return FALSE; + for (guint i = 0; i <= capsule_last; i++) { + g_autoptr(GError) error_local = NULL; + if (fu_uefi_cod_device_get_results_for_idx(device, i, &error_local)) + return TRUE; + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) && + !g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + } + + /* nothing found */ + return TRUE; +} + +static gboolean +fu_uefi_cod_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + g_autofree gchar *basename = NULL; + g_autofree gchar *cod_path = NULL; + g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self); + g_autoptr(GBytes) fw = NULL; + + /* ensure we have the existing state */ + if (fu_uefi_device_get_guid(self) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot update device info with no GUID"); + return FALSE; + } + + /* copy the capsule */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + basename = g_strdup_printf("fwupd-%s.cap", fu_uefi_device_get_guid(self)); + cod_path = g_build_filename(esp_path, "EFI", "UpdateCapsule", basename, NULL); + if (!fu_common_mkdir_parent(cod_path, error)) + return FALSE; + if (!fu_common_set_contents_bytes(cod_path, fw, error)) + return FALSE; + + /* + * NOTE: The EFI spec requires setting OsIndications! + * RT->SetVariable is not supported for all hardware, and so when using + * U-Boot, it applies the capsule even if OsIndications isn't set. + * The capsule is then deleted by U-Boot after it has been deployed. + */ + if (!fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE)) { + gsize bufsz = 0; + guint64 os_indications = 0; + g_autofree guint8 *buf = NULL; + if (!fu_efivar_get_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "OsIndications", + &buf, + &bufsz, + NULL, + error)) { + g_prefix_error(error, "failed to read EFI variable: "); + return FALSE; + } + if (!fu_common_read_uint64_safe(buf, + bufsz, + 0x0, + &os_indications, + G_LITTLE_ENDIAN, + error)) + return FALSE; + os_indications |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + if (!fu_efivar_set_data(FU_EFIVAR_GUID_EFI_GLOBAL, + "OsIndications", + (guint8 *)&os_indications, + sizeof(os_indications), + FU_EFIVAR_ATTR_NON_VOLATILE | + FU_EFIVAR_ATTR_BOOTSERVICE_ACCESS | + FU_EFIVAR_ATTR_RUNTIME_ACCESS, + error)) { + g_prefix_error(error, "Could not set OsIndications: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static void +fu_uefi_cod_device_init(FuUefiCodDevice *self) +{ +} + +static void +fu_uefi_cod_device_class_init(FuUefiCodDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->write_firmware = fu_uefi_cod_device_write_firmware; + klass_device->get_results = fu_uefi_cod_device_get_results; +} diff --git a/plugins/uefi-capsule/fu-uefi-cod-device.h b/plugins/uefi-capsule/fu-uefi-cod-device.h new file mode 100644 index 000000000..433a1515b --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-cod-device.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" + +#define FU_TYPE_UEFI_COD_DEVICE (fu_uefi_cod_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU, UEFI_COD_DEVICE, FuUefiDevice) + +struct _FuUefiCodDeviceClass { + FuUefiDeviceClass parent_class; +}; diff --git a/plugins/uefi-capsule/fu-uefi-common.h b/plugins/uefi-capsule/fu-uefi-common.h index 4369866c9..859cce78e 100644 --- a/plugins/uefi-capsule/fu-uefi-common.h +++ b/plugins/uefi-capsule/fu-uefi-common.h @@ -14,6 +14,8 @@ #define EFI_CAPSULE_HEADER_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define EFI_CAPSULE_HEADER_FLAGS_INITIATE_RESET 0x00040000 +#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004ULL + typedef struct __attribute__((__packed__)) { guint16 year; guint8 month; diff --git a/plugins/uefi-capsule/fu-uefi-device.c b/plugins/uefi-capsule/fu-uefi-device.c index 58c7066d9..7f3880646 100644 --- a/plugins/uefi-capsule/fu-uefi-device.c +++ b/plugins/uefi-capsule/fu-uefi-device.c @@ -15,7 +15,6 @@ #include "fu-uefi-common.h" #include "fu-uefi-device.h" #include "fu-uefi-devpath.h" -#include "fu-uefi-bootmgr.h" #include "fu-uefi-pcrs.h" typedef struct { @@ -78,8 +77,8 @@ fu_uefi_device_kind_to_string (FuUefiDeviceKind kind) return NULL; } -static FuUefiDeviceKind -fu_uefi_device_kind_from_string (const gchar *kind) +FuUefiDeviceKind +fu_uefi_device_kind_from_string(const gchar *kind) { if (g_strcmp0 (kind, "system-firmware") == 0) return FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE; @@ -217,6 +216,42 @@ fu_uefi_device_get_status (FuUefiDevice *self) return priv->last_attempt_status; } +void +fu_uefi_device_set_status(FuUefiDevice *self, FuUefiDeviceStatus status) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + g_autofree gchar *err_msg = NULL; + g_autofree gchar *version_str = NULL; + + g_return_if_fail(FU_IS_UEFI_DEVICE(self)); + + /* cache for later */ + priv->last_attempt_status = status; + + /* all good */ + if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) { + fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_SUCCESS); + return; + } + + /* something went wrong */ + if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC || + status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT) { + fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + } else { + fu_device_set_update_state(FU_DEVICE(self), FWUPD_UPDATE_STATE_FAILED); + } + version_str = g_strdup_printf("%u", priv->last_attempt_version); + tmp = fu_uefi_device_status_to_string(status); + if (tmp == NULL) { + err_msg = g_strdup_printf("failed to update to %s", version_str); + } else { + err_msg = g_strdup_printf("failed to update to %s: %s", version_str, tmp); + } + fu_device_set_update_error(FU_DEVICE(self), err_msg); +} + guint32 fu_uefi_device_get_capsule_flags (FuUefiDevice *self) { @@ -233,8 +268,8 @@ fu_uefi_device_get_guid (FuUefiDevice *self) return priv->fw_class; } -static gchar * -fu_uefi_device_build_varname (FuUefiDevice *self) +gchar * +fu_uefi_device_build_varname(FuUefiDevice *self) { FuUefiDevicePrivate *priv = GET_PRIVATE (self); return g_strdup_printf ("fwupd-%s-%"G_GUINT64_FORMAT, @@ -356,10 +391,9 @@ fu_uefi_device_build_dp_buf (const gchar *path, gsize *bufsz, GError **error) return g_steal_pointer (&dp_buf); } -static GBytes * -fu_uefi_device_fixup_firmware (FuDevice *device, GBytes *fw, GError **error) +GBytes * +fu_uefi_device_fixup_firmware(FuUefiDevice *self, GBytes *fw, GError **error) { - FuUefiDevice *self = FU_UEFI_DEVICE (device); FuUefiDevicePrivate *priv = GET_PRIVATE (self); gsize fw_length; const guint8 *data = g_bytes_get_data (fw, &fw_length); @@ -555,82 +589,6 @@ fu_uefi_device_cleanup (FuDevice *device, return TRUE; } -static gboolean -fu_uefi_device_write_firmware (FuDevice *device, - FuFirmware *firmware, - FwupdInstallFlags install_flags, - GError **error) -{ - FuUefiDevice *self = FU_UEFI_DEVICE (device); - FuUefiDevicePrivate *priv = GET_PRIVATE (self); - FuUefiBootmgrFlags flags = FU_UEFI_BOOTMGR_FLAG_NONE; - const gchar *bootmgr_desc = "Linux Firmware Updater"; - g_autofree gchar *esp_path = fu_volume_get_mount_point (priv->esp); - g_autoptr(GBytes) fixed_fw = NULL; - g_autoptr(GBytes) fw = NULL; - g_autofree gchar *basename = NULL; - g_autofree gchar *directory = NULL; - g_autofree gchar *fn = NULL; - g_autofree gchar *varname = fu_uefi_device_build_varname (self); - - /* ensure we have the existing state */ - if (priv->fw_class == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "cannot update device info with no GUID"); - return FALSE; - } - - /* get default image */ - fw = fu_firmware_get_bytes (firmware, error); - if (fw == NULL) - return FALSE; - - /* save the blob to the ESP */ - directory = fu_uefi_get_esp_path_for_os (device, esp_path); - basename = g_strdup_printf ("fwupd-%s.cap", priv->fw_class); - fn = g_build_filename (directory, "fw", basename, NULL); - if (!fu_common_mkdir_parent (fn, error)) - return FALSE; - fixed_fw = fu_uefi_device_fixup_firmware (device, fw, error); - if (fixed_fw == NULL) - return FALSE; - if (!fu_common_set_contents_bytes (fn, fixed_fw, error)) - return FALSE; - - /* delete the logs to save space; use fwupdate to debug the EFI binary */ - if (fu_efivar_exists (FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE")) { - if (!fu_efivar_delete (FU_EFIVAR_GUID_FWUPDATE, - "FWUPDATE_VERBOSE", error)) - return FALSE; - } - if (fu_efivar_exists (FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG")) { - if (!fu_efivar_delete (FU_EFIVAR_GUID_FWUPDATE, - "FWUPDATE_DEBUG_LOG", error)) - return FALSE; - } - - /* set the blob header shared with fwupd.efi */ - if (!fu_uefi_device_write_update_info (self, fn, varname, priv->fw_class, error)) - return FALSE; - - /* update the firmware before the bootloader runs */ - if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB)) - flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB; - if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE)) - flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE; - - /* some legacy devices use the old name to deduplicate boot entries */ - if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC)) - bootmgr_desc = "Linux-Firmware-Updater"; - if (!fu_uefi_bootmgr_bootnext (device, esp_path, bootmgr_desc, flags, error)) - return FALSE; - - /* success! */ - return TRUE; -} - static gboolean fu_uefi_device_add_system_checksum (FuDevice *device, GError **error) { @@ -759,54 +717,21 @@ fu_uefi_device_probe (FuDevice *device, GError **error) static gboolean fu_uefi_device_get_results (FuDevice *device, GError **error) { - FuUefiDevice *device_uefi = FU_UEFI_DEVICE (device); - FuUefiDeviceStatus status = fu_uefi_device_get_status (device_uefi); - const gchar *tmp; - g_autofree gchar *err_msg = NULL; - g_autofree gchar *version_str = NULL; - g_autoptr(GError) error_local = NULL; + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiDevicePrivate *priv = GET_PRIVATE(self); - /* trivial case */ - if (status == FU_UEFI_DEVICE_STATUS_SUCCESS) { - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS); - return TRUE; - } - - /* check if something rudely removed our BOOTXXXX entry */ - if (!fu_uefi_bootmgr_verify_fwupd (&error_local)) { - if (fu_device_has_private_flag (device, FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK)) { - g_prefix_error (&error_local, - "boot entry missing; " - "perhaps 'Boot Order Lock' enabled in the BIOS: "); - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); - } else { - g_prefix_error (&error_local, "boot entry missing: "); - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); - } - fu_device_set_update_error (device, error_local->message); - return TRUE; - } - - /* something went wrong */ - if (status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_AC || - status == FU_UEFI_DEVICE_STATUS_ERROR_PWR_EVT_BATT) { - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); - } else { - fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED); - } - version_str = g_strdup_printf ("%u", fu_uefi_device_get_version_error (device_uefi)); - tmp = fu_uefi_device_status_to_string (status); - if (tmp == NULL) { - err_msg = g_strdup_printf ("failed to update to %s", - version_str); - } else { - err_msg = g_strdup_printf ("failed to update to %s: %s", - version_str, tmp); - } - fu_device_set_update_error (device, err_msg); + /* just set the update error */ + fu_uefi_device_set_status(self, priv->last_attempt_status); return TRUE; } +gchar * +fu_uefi_device_get_esp_path(FuUefiDevice *self) +{ + FuUefiDevicePrivate *priv = GET_PRIVATE(self); + return fu_volume_get_mount_point(priv->esp); +} + static void fu_uefi_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -830,7 +755,7 @@ fu_uefi_device_set_property (GObject *object, guint prop_id, priv->fw_version_lowest = g_value_get_uint (value); break; case PROP_LAST_ATTEMPT_STATUS: - priv->last_attempt_status = g_value_get_uint (value); + fu_uefi_device_set_status(self, g_value_get_uint(value)); break; case PROP_LAST_ATTEMPT_VERSION: priv->last_attempt_version = g_value_get_uint (value); @@ -867,6 +792,9 @@ fu_uefi_device_init (FuUefiDevice *self) fu_device_register_private_flag (FU_DEVICE (self), FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH, "fallback-to-removable-path"); + fu_device_register_private_flag(FU_DEVICE(self), + FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE, + "no-rt-set-variable"); } static void @@ -896,7 +824,6 @@ fu_uefi_device_class_init (FuUefiDeviceClass *klass) klass_device->to_string = fu_uefi_device_to_string; klass_device->probe = fu_uefi_device_probe; klass_device->prepare = fu_uefi_device_prepare; - klass_device->write_firmware = fu_uefi_device_write_firmware; klass_device->cleanup = fu_uefi_device_cleanup; klass_device->report_metadata_pre = fu_uefi_device_report_metadata_pre; klass_device->report_metadata_post = fu_uefi_device_report_metadata_post; @@ -954,37 +881,3 @@ fu_uefi_device_class_init (FuUefiDeviceClass *klass) G_PARAM_STATIC_NAME); g_object_class_install_property (object_class, PROP_FMP_HARDWARE_INSTANCE, pspec); } - -FuUefiDevice * -fu_uefi_device_new_from_dev (FuDevice *dev) -{ - const gchar *tmp; - FuUefiDevice *self; - FuUefiDevicePrivate *priv; - - g_return_val_if_fail (fu_device_get_guid_default (dev) != NULL, NULL); - - /* create virtual object not backed by an ESRT entry */ - self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); - fu_device_incorporate (FU_DEVICE (self), dev); - priv = GET_PRIVATE (self); - priv->fw_class = g_strdup (fu_device_get_guid_default (dev)); - tmp = fu_device_get_metadata (dev, FU_DEVICE_METADATA_UEFI_DEVICE_KIND); - priv->kind = fu_uefi_device_kind_from_string (tmp); - priv->capsule_flags = fu_device_get_metadata_integer (dev, FU_DEVICE_METADATA_UEFI_CAPSULE_FLAGS); - priv->fw_version = fu_device_get_metadata_integer (dev, FU_DEVICE_METADATA_UEFI_FW_VERSION); - g_assert (priv->fw_class != NULL); - return self; -} - -FuUefiDevice * -fu_uefi_device_new_from_guid (const gchar *guid) -{ - FuUefiDevice *self; - FuUefiDevicePrivate *priv; - self = g_object_new (FU_TYPE_UEFI_DEVICE, NULL); - priv = GET_PRIVATE (self); - priv->fw_class = g_strdup (guid); - fu_device_set_version_format (FU_DEVICE (self), FWUPD_VERSION_FORMAT_NUMBER); - return self; -} diff --git a/plugins/uefi-capsule/fu-uefi-device.h b/plugins/uefi-capsule/fu-uefi-device.h index f5baf4b62..c9cdd391e 100644 --- a/plugins/uefi-capsule/fu-uefi-device.h +++ b/plugins/uefi-capsule/fu-uefi-device.h @@ -79,15 +79,26 @@ typedef enum { * Use shim to load fwupdx64.efi when SecureBoot is turned on. */ #define FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB (1 << 5) +/** + * FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE: + * + * Do not use RT->SetVariable. + */ +#define FU_UEFI_DEVICE_FLAG_NO_RT_SET_VARIABLE (1 << 6) + +FuUefiDeviceKind +fu_uefi_device_kind_from_string(const gchar *kind); -FuUefiDevice *fu_uefi_device_new_from_guid (const gchar *guid); -FuUefiDevice *fu_uefi_device_new_from_dev (FuDevice *dev); void fu_uefi_device_set_esp (FuUefiDevice *self, FuVolume *esp); gboolean fu_uefi_device_clear_status (FuUefiDevice *self, GError **error); FuUefiDeviceKind fu_uefi_device_get_kind (FuUefiDevice *self); const gchar *fu_uefi_device_get_guid (FuUefiDevice *self); +gchar * +fu_uefi_device_get_esp_path(FuUefiDevice *self); +gchar * +fu_uefi_device_build_varname(FuUefiDevice *self); guint32 fu_uefi_device_get_version (FuUefiDevice *self); guint32 fu_uefi_device_get_version_lowest (FuUefiDevice *self); guint32 fu_uefi_device_get_version_error (FuUefiDevice *self); @@ -103,3 +114,7 @@ gboolean fu_uefi_device_write_update_info (FuUefiDevice *self, const gchar *varname, const gchar *guid, GError **error); +GBytes * +fu_uefi_device_fixup_firmware(FuUefiDevice *self, GBytes *fw, GError **error); +void +fu_uefi_device_set_status(FuUefiDevice *self, FuUefiDeviceStatus status); diff --git a/plugins/uefi-capsule/fu-uefi-nvram-device.c b/plugins/uefi-capsule/fu-uefi-nvram-device.c new file mode 100644 index 000000000..f453eefb6 --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-nvram-device.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2018 Richard Hughes + * Copyright (C) 2018 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-uefi-bootmgr.h" +#include "fu-uefi-common.h" +#include "fu-uefi-nvram-device.h" + +struct _FuUefiNvramDevice { + FuUefiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuUefiNvramDevice, fu_uefi_nvram_device, FU_TYPE_UEFI_DEVICE) + +static gboolean +fu_uefi_nvram_device_get_results(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* check if something rudely removed our BOOTXXXX entry */ + if (!fu_uefi_bootmgr_verify_fwupd(&error_local)) { + if (fu_device_has_private_flag(device, + FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK)) { + g_prefix_error(&error_local, + "boot entry missing; " + "perhaps 'Boot Order Lock' enabled in the BIOS: "); + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + } else { + g_prefix_error(&error_local, "boot entry missing: "); + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED); + } + fu_device_set_update_error(device, error_local->message); + return TRUE; + } + + /* parent */ + return FU_DEVICE_CLASS(fu_uefi_nvram_device_parent_class)->get_results(device, error); +} + +static gboolean +fu_uefi_nvram_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FwupdInstallFlags flags, + GError **error) +{ + FuUefiDevice *self = FU_UEFI_DEVICE(device); + FuUefiBootmgrFlags bootmgr_flags = FU_UEFI_BOOTMGR_FLAG_NONE; + const gchar *bootmgr_desc = "Linux Firmware Updater"; + const gchar *fw_class = fu_uefi_device_get_guid(self); + g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self); + g_autoptr(GBytes) fixed_fw = NULL; + g_autoptr(GBytes) fw = NULL; + g_autofree gchar *basename = NULL; + g_autofree gchar *directory = NULL; + g_autofree gchar *fn = NULL; + g_autofree gchar *varname = fu_uefi_device_build_varname(self); + + /* ensure we have the existing state */ + if (fw_class == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "cannot update device info with no GUID"); + return FALSE; + } + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* save the blob to the ESP */ + directory = fu_uefi_get_esp_path_for_os(device, esp_path); + basename = g_strdup_printf("fwupd-%s.cap", fw_class); + fn = g_build_filename(directory, "fw", basename, NULL); + if (!fu_common_mkdir_parent(fn, error)) + return FALSE; + fixed_fw = fu_uefi_device_fixup_firmware(self, fw, error); + if (fixed_fw == NULL) + return FALSE; + if (!fu_common_set_contents_bytes(fn, fixed_fw, error)) + return FALSE; + + /* delete the logs to save space; use fwupdate to debug the EFI binary */ + if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE")) { + if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_VERBOSE", error)) + return FALSE; + } + if (fu_efivar_exists(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG")) { + if (!fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "FWUPDATE_DEBUG_LOG", error)) + return FALSE; + } + + /* set the blob header shared with fwupd.efi */ + if (!fu_uefi_device_write_update_info(self, fn, varname, fw_class, error)) + return FALSE; + + /* update the firmware before the bootloader runs */ + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB)) + bootmgr_flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_FOR_SB; + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_UNIQUE)) + bootmgr_flags |= FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE; + + /* some legacy devices use the old name to deduplicate boot entries */ + if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC)) + bootmgr_desc = "Linux-Firmware-Updater"; + if (!fu_uefi_bootmgr_bootnext(device, esp_path, bootmgr_desc, bootmgr_flags, error)) + return FALSE; + + /* success! */ + return TRUE; +} + +static void +fu_uefi_nvram_device_init(FuUefiNvramDevice *self) +{ +} + +static void +fu_uefi_nvram_device_class_init(FuUefiNvramDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + klass_device->get_results = fu_uefi_nvram_device_get_results; + klass_device->write_firmware = fu_uefi_nvram_device_write_firmware; +} diff --git a/plugins/uefi-capsule/fu-uefi-nvram-device.h b/plugins/uefi-capsule/fu-uefi-nvram-device.h new file mode 100644 index 000000000..91642c0be --- /dev/null +++ b/plugins/uefi-capsule/fu-uefi-nvram-device.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-uefi-device.h" + +#define FU_TYPE_UEFI_NVRAM_DEVICE (fu_uefi_nvram_device_get_type()) +G_DECLARE_FINAL_TYPE(FuUefiNvramDevice, fu_uefi_nvram_device, FU, UEFI_NVRAM_DEVICE, FuUefiDevice) + +struct _FuUefiNvramDeviceClass { + FuUefiDeviceClass parent_class; +}; diff --git a/plugins/uefi-capsule/fu-uefi-tool.c b/plugins/uefi-capsule/fu-uefi-tool.c index 14a697f43..d48e21c91 100644 --- a/plugins/uefi-capsule/fu-uefi-tool.c +++ b/plugins/uefi-capsule/fu-uefi-tool.c @@ -7,8 +7,9 @@ #include "config.h" #include -#include + #include +#include #include #include #include @@ -16,8 +17,9 @@ #include "fu-context-private.h" #include "fu-ucs2.h" #include "fu-uefi-backend.h" +#include "fu-uefi-cod-device.h" #include "fu-uefi-common.h" -#include "fu-uefi-device.h" +#include "fu-uefi-nvram-device.h" #include "fu-uefi-update-info.h" /* custom return code */ @@ -62,6 +64,7 @@ main (int argc, char *argv[]) gboolean ret; gboolean verbose = FALSE; g_autofree gchar *apply = FALSE; + g_autofree gchar *type = FALSE; g_autofree gchar *esp_path = NULL; g_autofree gchar *flags = FALSE; g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1); @@ -69,48 +72,113 @@ main (int argc, char *argv[]) g_autoptr(GPtrArray) devices = NULL; g_autoptr(FuVolume) esp = NULL; const GOptionEntry options[] = { - { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, - /* TRANSLATORS: command line option */ - _("Show extra debugging information"), NULL }, - { "version", '\0', 0, G_OPTION_ARG_NONE, &action_version, - /* TRANSLATORS: command line option */ - _("Display version"), NULL }, - { "log", 'L', 0, G_OPTION_ARG_NONE, &action_log, - /* TRANSLATORS: command line option */ - _("Show the debug log from the last attempted update"), NULL }, - { "list", 'l', 0, G_OPTION_ARG_NONE, &action_list, - /* TRANSLATORS: command line option */ - _("List supported firmware updates"), NULL }, - { "supported", 's', 0, G_OPTION_ARG_NONE, &action_supported, - /* TRANSLATORS: command line option */ - _("Query for firmware update support"), NULL }, - { "info", 'i', 0, G_OPTION_ARG_NONE, &action_info, - /* TRANSLATORS: command line option */ - _("Show the information of firmware update status"), NULL }, - { "enable", 'e', 0, G_OPTION_ARG_NONE, &action_enable, - /* TRANSLATORS: command line option */ - _("Enable firmware update support on supported systems"), NULL }, - { "esp-path", 'p', 0, G_OPTION_ARG_STRING, &esp_path, - /* TRANSLATORS: command line option */ - _("Override the default ESP path"), - /* TRANSLATORS: command argument: uppercase, spaces->dashes */ - _("PATH") }, - { "set-debug", 'd', 0, G_OPTION_ARG_NONE, &action_set_debug, - /* TRANSLATORS: command line option */ - _("Set the debugging flag during update"), NULL }, - { "unset-debug", 'D', 0, G_OPTION_ARG_NONE, &action_unset_debug, - /* TRANSLATORS: command line option */ - _("Unset the debugging flag during update"), NULL }, - { "apply", 'a', 0, G_OPTION_ARG_STRING, &apply, - /* TRANSLATORS: command line option */ - _("Apply firmware updates"), - /* TRANSLATORS: command argument: uppercase, spaces->dashes */ - C_("A single GUID", "GUID") }, - { "flags", 'f', 0, G_OPTION_ARG_STRING, &flags, - /* TRANSLATORS: command line option */ - _("Use quirk flags when installing firmware"), NULL }, - { NULL} - }; + {"verbose", + 'v', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &verbose, + /* TRANSLATORS: command line option */ + _("Show extra debugging information"), + NULL}, + {"version", + '\0', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_version, + /* TRANSLATORS: command line option */ + _("Display version"), + NULL}, + {"log", + 'L', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_log, + /* TRANSLATORS: command line option */ + _("Show the debug log from the last attempted update"), + NULL}, + {"list", + 'l', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_list, + /* TRANSLATORS: command line option */ + _("List supported firmware updates"), + NULL}, + {"supported", + 's', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_supported, + /* TRANSLATORS: command line option */ + _("Query for firmware update support"), + NULL}, + {"info", + 'i', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_info, + /* TRANSLATORS: command line option */ + _("Show the information of firmware update status"), + NULL}, + {"enable", + 'e', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_enable, + /* TRANSLATORS: command line option */ + _("Enable firmware update support on supported systems"), + NULL}, + {"esp-path", + 'p', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &esp_path, + /* TRANSLATORS: command line option */ + _("Override the default ESP path"), + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("PATH")}, + {"set-debug", + 'd', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_set_debug, + /* TRANSLATORS: command line option */ + _("Set the debugging flag during update"), + NULL}, + {"unset-debug", + 'D', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_NONE, + &action_unset_debug, + /* TRANSLATORS: command line option */ + _("Unset the debugging flag during update"), + NULL}, + {"apply", + 'a', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &apply, + /* TRANSLATORS: command line option */ + _("Apply firmware updates"), + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + C_("A single GUID", "GUID")}, + {"method", + 'm', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &type, + /* TRANSLATORS: command line option */ + _("Device update method"), + "nvram|cod"}, + {"flags", + 'f', + G_OPTION_FLAG_NONE, + G_OPTION_ARG_STRING, + &flags, + /* TRANSLATORS: command line option */ + _("Use quirk flags when installing firmware"), + NULL}, + {NULL}}; setlocale (LC_ALL, ""); @@ -316,10 +384,26 @@ main (int argc, char *argv[]) /* apply firmware updates */ if (apply != NULL) { - g_autoptr(FuUefiDevice) dev = fu_uefi_device_new_from_guid (apply); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuBackend) backend = fu_uefi_backend_new(ctx); + g_autoptr(FuUefiDevice) dev = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GBytes) fw = NULL; + /* type is specified, otherwise use default */ + if (type != NULL) { + if (g_strcmp0(type, "nvram") == 0) { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend), + FU_TYPE_UEFI_NVRAM_DEVICE); + } else if (g_strcmp0(type, "cod") == 0) { + fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(backend), + FU_TYPE_UEFI_COD_DEVICE); + } else { + g_printerr("invalid type specified\n"); + return EXIT_FAILURE; + } + } + if (argv[1] == NULL) { g_printerr ("capsule filename required\n"); return EXIT_FAILURE; @@ -329,6 +413,7 @@ main (int argc, char *argv[]) g_printerr ("failed: %s\n", error_local->message); return EXIT_FAILURE; } + dev = fu_uefi_backend_device_new_from_guid(FU_UEFI_BACKEND(backend), apply); fu_uefi_device_set_esp (dev, esp); if (flags != NULL) fu_device_set_custom_flags (FU_DEVICE (dev), flags); diff --git a/plugins/uefi-capsule/meson.build b/plugins/uefi-capsule/meson.build index d2b60c26d..b83789026 100644 --- a/plugins/uefi-capsule/meson.build +++ b/plugins/uefi-capsule/meson.build @@ -10,7 +10,7 @@ endif install_data(['uefi-capsule.quirk'], install_dir: join_paths(datadir, 'fwupd', 'quirks.d')) -backend_srcs = [] +backend_srcs = ['fu-uefi-backend.c'] if host_machine.system() == 'linux' backend_srcs += 'fu-uefi-backend-linux.c' elif host_machine.system() == 'freebsd' @@ -27,6 +27,8 @@ shared_module('fu_plugin_uefi_capsule', 'fu-ucs2.c', 'fu-uefi-bootmgr.c', 'fu-uefi-common.c', + 'fu-uefi-cod-device.c', + 'fu-uefi-nvram-device.c', 'fu-uefi-device.c', 'fu-uefi-devpath.c', 'fu-uefi-pcrs.c', @@ -64,6 +66,8 @@ fwupdate = executable( 'fu-uefi-bootmgr.c', 'fu-uefi-common.c', 'fu-uefi-device.c', + 'fu-uefi-cod-device.c', + 'fu-uefi-nvram-device.c', 'fu-uefi-devpath.c', 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', @@ -145,6 +149,8 @@ if get_option('tests') 'fu-uefi-bootmgr.c', 'fu-uefi-common.c', 'fu-uefi-device.c', + 'fu-uefi-cod-device.c', + 'fu-uefi-nvram-device.c', 'fu-uefi-devpath.c', 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', diff --git a/plugins/uefi-capsule/tests/efi/efivars/Capsule0000-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/plugins/uefi-capsule/tests/efi/efivars/Capsule0000-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 000000000..834766aeb Binary files /dev/null and b/plugins/uefi-capsule/tests/efi/efivars/Capsule0000-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/plugins/uefi-capsule/tests/efi/efivars/Capsule0001-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/plugins/uefi-capsule/tests/efi/efivars/Capsule0001-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 000000000..7655990f5 Binary files /dev/null and b/plugins/uefi-capsule/tests/efi/efivars/Capsule0001-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/plugins/uefi-capsule/tests/efi/efivars/CapsuleLast-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/plugins/uefi-capsule/tests/efi/efivars/CapsuleLast-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 000000000..2f697464d Binary files /dev/null and b/plugins/uefi-capsule/tests/efi/efivars/CapsuleLast-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/plugins/uefi-capsule/tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3 b/plugins/uefi-capsule/tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3 new file mode 100644 index 000000000..9e6766a5a Binary files /dev/null and b/plugins/uefi-capsule/tests/efi/efivars/CapsuleMax-39b68c46-f7fb-441b-b6ec-16b0f69821f3 differ diff --git a/plugins/uefi-capsule/uefi_capsule.conf b/plugins/uefi-capsule/uefi_capsule.conf index c543a7f24..6dbd4dc6a 100644 --- a/plugins/uefi-capsule/uefi_capsule.conf +++ b/plugins/uefi-capsule/uefi_capsule.conf @@ -14,3 +14,6 @@ # with the UEFI removable path enabled, the default esp path is set to /EFI/boot # the shim EFI binary and presumably this is $ESP/EFI/boot/bootx64.efi #FallbacktoRemovablePath=false + +# allow ignoring the CapsuleOnDisk support advertised by the firmware +#DisableCapsuleUpdateOnDisk=true