/* * Copyright (C) 2016 Richard Hughes * Copyright (C) 2015 Peter Jones * * SPDX-License-Identifier: LGPL-2.1+ */ #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-grub-device.h" struct FuPluginData { FuUefiBgrt *bgrt; FuVolume *esp; FuBackend *backend; }; void fu_plugin_init(FuPlugin *plugin) { FuContext *ctx = fu_plugin_get_context(plugin); FuPluginData *data = fu_plugin_alloc_data(plugin, sizeof(FuPluginData)); data->backend = fu_uefi_backend_new(ctx); data->bgrt = fu_uefi_bgrt_new(); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "tpm_eventlog"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "dell"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "acpi_phat"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "uefi"); /* old name */ fu_plugin_set_build_hash(plugin, FU_BUILD_HASH); } void fu_plugin_destroy(FuPlugin *plugin) { FuPluginData *data = fu_plugin_get_data(plugin); if (data->esp != NULL) g_object_unref(data->esp); g_object_unref(data->backend); g_object_unref(data->bgrt); } gboolean fu_plugin_clear_results(FuPlugin *plugin, FuDevice *device, GError **error) { FuUefiDevice *device_uefi = FU_UEFI_DEVICE(device); return fu_uefi_device_clear_status(device_uefi, error); } void fu_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) { g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GError) error = NULL; /* create attr */ attr = fwupd_security_attr_new(FWUPD_SECURITY_ATTR_ID_UEFI_SECUREBOOT); fwupd_security_attr_set_plugin(attr, fu_plugin_get_name(plugin)); fu_security_attrs_append(attrs, attr); /* SB not available or disabled */ if (!fu_efivar_secure_boot_enabled_full(&error)) { if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_FOUND); return; } fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE); fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED); return; } /* success */ fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_ENABLED); } static GBytes * fu_plugin_uefi_capsule_get_splash_data(guint width, guint height, GError **error) { const gchar *const *langs = g_get_language_names(); g_autofree gchar *datadir_pkg = NULL; g_autofree gchar *filename_archive = NULL; g_autofree gchar *langs_str = NULL; g_autoptr(FuArchive) archive = NULL; g_autoptr(GBytes) blob_archive = NULL; /* load archive */ datadir_pkg = fu_common_get_path(FU_PATH_KIND_DATADIR_PKG); filename_archive = g_build_filename(datadir_pkg, "uefi-capsule-ux.tar.xz", NULL); blob_archive = fu_common_get_contents_bytes(filename_archive, error); if (blob_archive == NULL) return NULL; archive = fu_archive_new(blob_archive, FU_ARCHIVE_FLAG_NONE, error); if (archive == NULL) return NULL; /* find the closest locale match, falling back to `en` and `C` */ for (guint i = 0; langs[i] != NULL; i++) { GBytes *blob_tmp; g_autofree gchar *fn = NULL; if (g_str_has_suffix(langs[i], ".UTF-8")) continue; fn = g_strdup_printf("fwupd-%s-%u-%u.bmp", langs[i], width, height); blob_tmp = fu_archive_lookup_by_fn(archive, fn, NULL); if (blob_tmp != NULL) { g_debug("using UX image %s", fn); return g_bytes_ref(blob_tmp); } g_debug("no %s found", fn); } /* we found nothing */ langs_str = g_strjoinv(",", (gchar **)langs); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "failed to get splash file for %s in %s", langs_str, datadir_pkg); return NULL; } static guint8 fu_plugin_uefi_capsule_calc_checksum(const guint8 *buf, gsize sz) { guint8 csum = 0; for (gsize i = 0; i < sz; i++) csum += buf[i]; return csum; } static gboolean fu_plugin_uefi_capsule_write_splash_data(FuPlugin *plugin, FuDevice *device, GBytes *blob, GError **error) { FuPluginData *data = fu_plugin_get_data(plugin); guint32 screen_x, screen_y; gsize buf_size = g_bytes_get_size(blob); gssize size; guint32 height, width; guint8 csum = 0; efi_ux_capsule_header_t header = {0}; efi_capsule_header_t capsule_header = {.flags = EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET, .guid = {0x0}, .header_size = sizeof(efi_capsule_header_t), .capsule_image_size = 0}; g_autofree gchar *esp_path = NULL; g_autofree gchar *fn = NULL; g_autofree gchar *directory = NULL; g_autofree gchar *basename = NULL; g_autoptr(GFile) ofile = NULL; g_autoptr(GOutputStream) ostream = NULL; /* get screen dimensions */ if (!fu_uefi_get_framebuffer_size(&screen_x, &screen_y, error)) return FALSE; if (!fu_uefi_get_bitmap_size((const guint8 *)g_bytes_get_data(blob, NULL), buf_size, &width, &height, error)) { g_prefix_error(error, "splash invalid: "); return FALSE; } /* save to a predicatable filename */ esp_path = fu_volume_get_mount_point(data->esp); directory = fu_uefi_get_esp_path_for_os(device, esp_path); basename = g_strdup_printf("fwupd-%s.cap", FU_EFIVAR_GUID_UX_CAPSULE); fn = g_build_filename(directory, "fw", basename, NULL); if (!fu_common_mkdir_parent(fn, error)) return FALSE; ofile = g_file_new_for_path(fn); ostream = G_OUTPUT_STREAM(g_file_replace(ofile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); if (ostream == NULL) return FALSE; if (!fwupd_guid_from_string(FU_EFIVAR_GUID_UX_CAPSULE, &capsule_header.guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) return FALSE; capsule_header.capsule_image_size = g_bytes_get_size(blob) + sizeof(efi_capsule_header_t) + sizeof(efi_ux_capsule_header_t); header.version = 1; header.image_type = 0; header.reserved = 0; header.x_offset = (screen_x / 2) - (width / 2); header.y_offset = fu_uefi_bgrt_get_yoffset(data->bgrt) + fu_uefi_bgrt_get_height(data->bgrt); /* header, payload and image has to add to zero */ csum += fu_plugin_uefi_capsule_calc_checksum((guint8 *)&capsule_header, sizeof(capsule_header)); csum += fu_plugin_uefi_capsule_calc_checksum((guint8 *)&header, sizeof(header)); csum += fu_plugin_uefi_capsule_calc_checksum(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); header.checksum = 0x100 - csum; /* write capsule file */ size = g_output_stream_write(ostream, &capsule_header, capsule_header.header_size, NULL, error); if (size < 0) return FALSE; size = g_output_stream_write(ostream, &header, sizeof(header), NULL, error); if (size < 0) return FALSE; size = g_output_stream_write_bytes(ostream, blob, NULL, error); if (size < 0) return FALSE; /* write display capsule location as UPDATE_INFO */ return fu_uefi_device_write_update_info(FU_UEFI_DEVICE(device), fn, "fwupd-ux-capsule", FU_EFIVAR_GUID_UX_CAPSULE, error); } static gboolean fu_plugin_uefi_capsule_update_splash(FuPlugin *plugin, FuDevice *device, GError **error) { FuPluginData *data = fu_plugin_get_data(plugin); guint best_idx = G_MAXUINT; guint32 lowest_border_pixels = G_MAXUINT; guint32 screen_height = 768; guint32 screen_width = 1024; g_autoptr(GBytes) image_bmp = NULL; struct { guint32 width; guint32 height; } sizes[] = {{640, 480}, /* matching the sizes in po/make-images */ {800, 600}, {1024, 768}, {1920, 1080}, {3840, 2160}, {5120, 2880}, {5688, 3200}, {7680, 4320}, {0, 0}}; /* no UX capsule support, so deleting var if it exists */ if (fu_device_has_private_flag(device, FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE)) { g_debug("not providing UX capsule"); return fu_efivar_delete(FU_EFIVAR_GUID_FWUPDATE, "fwupd-ux-capsule", error); } /* get the boot graphics resource table data */ if (!fu_uefi_bgrt_get_supported(data->bgrt)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "BGRT is not supported"); return FALSE; } if (!fu_uefi_get_framebuffer_size(&screen_width, &screen_height, error)) return FALSE; g_debug("framebuffer size %" G_GUINT32_FORMAT " x%" G_GUINT32_FORMAT, screen_width, screen_height); /* find the 'best sized' pre-generated image */ for (guint i = 0; sizes[i].width != 0; i++) { guint32 border_pixels; /* disregard any images that are bigger than the screen */ if (sizes[i].width > screen_width) continue; if (sizes[i].height > screen_height) continue; /* is this the best fit for the display */ border_pixels = (screen_width * screen_height) - (sizes[i].width * sizes[i].height); if (border_pixels < lowest_border_pixels) { lowest_border_pixels = border_pixels; best_idx = i; } } if (best_idx == G_MAXUINT) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "failed to find a suitable image to use"); return FALSE; } /* get the raw data */ image_bmp = fu_plugin_uefi_capsule_get_splash_data(sizes[best_idx].width, sizes[best_idx].height, error); if (image_bmp == NULL) return FALSE; /* perform the upload */ return fu_plugin_uefi_capsule_write_splash_data(plugin, device, image_bmp, error); } gboolean fu_plugin_write_firmware(FuPlugin *plugin, FuDevice *device, GBytes *blob_fw, FwupdInstallFlags flags, GError **error) { const gchar *str; guint32 flashes_left; g_autoptr(GError) error_splash = NULL; /* test the flash counter */ flashes_left = fu_device_get_flashes_left(device); if (flashes_left > 0) { g_debug("%s has %" G_GUINT32_FORMAT " flashes left", fu_device_get_name(device), flashes_left); if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && flashes_left <= 2) { g_set_error( error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "%s only has %" G_GUINT32_FORMAT " flashes left -- " "see https://github.com/fwupd/fwupd/wiki/Dell-TPM:-flashes-left for " "more information.", fu_device_get_name(device), flashes_left); return FALSE; } } /* TRANSLATORS: this is shown when updating the firmware after the reboot */ str = _("Installing firmware update…"); g_assert(str != NULL); /* perform the update */ fu_device_set_status(device, FWUPD_STATUS_SCHEDULING); if (!fu_plugin_uefi_capsule_update_splash(plugin, device, &error_splash)) { g_debug("failed to upload UEFI UX capsule text: %s", error_splash->message); } return fu_device_write_firmware(device, blob_fw, flags, error); } static void fu_plugin_uefi_capsule_load_config(FuPlugin *plugin, FuDevice *device) { gboolean disable_shim; gboolean fallback_removable_path; guint64 sz_reqd = FU_UEFI_COMMON_REQUIRED_ESP_FREE_SPACE; g_autofree gchar *require_esp_free_space = NULL; /* parse free space needed for ESP */ require_esp_free_space = fu_plugin_get_config_value(plugin, "RequireESPFreeSpace"); if (require_esp_free_space != NULL) sz_reqd = fu_common_strtoull(require_esp_free_space); fu_device_set_metadata_integer(device, "RequireESPFreeSpace", sz_reqd); /* shim used for SB or not? */ disable_shim = fu_plugin_get_config_value_boolean(plugin, "DisableShimForSecureBoot"); if (!disable_shim) fu_device_add_private_flag(device, FU_UEFI_DEVICE_FLAG_USE_SHIM_FOR_SB); /* check if using UEFI removable path */ fallback_removable_path = fu_plugin_get_config_value_boolean(plugin, "FallbacktoRemovablePath"); if (fallback_removable_path) fu_device_add_private_flag(device, FU_UEFI_DEVICE_FLAG_FALLBACK_TO_REMOVABLE_PATH); } static void fu_plugin_uefi_capsule_register_proxy_device(FuPlugin *plugin, FuDevice *device) { FuPluginData *data = fu_plugin_get_data(plugin); 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); if (data->esp == NULL) { fu_device_set_update_error(FU_DEVICE(dev), error_local->message); fu_device_remove_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_UPDATABLE); } else { fu_uefi_device_set_esp(dev, data->esp); } fu_plugin_device_add(plugin, FU_DEVICE(dev)); } void fu_plugin_device_registered(FuPlugin *plugin, FuDevice *device) { if (fu_device_get_metadata(device, FU_DEVICE_METADATA_UEFI_DEVICE_KIND) != NULL) { if (fu_device_get_guid_default(device) == NULL) { g_autofree gchar *dbg = fu_device_to_string(device); g_warning("cannot create proxy device as no GUID: %s", dbg); return; } fu_plugin_uefi_capsule_register_proxy_device(plugin, device); } } static const gchar * fu_plugin_uefi_capsule_uefi_type_to_string(FuUefiDeviceKind device_kind) { if (device_kind == FU_UEFI_DEVICE_KIND_UNKNOWN) return "Unknown Firmware"; if (device_kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) return "System Firmware"; if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) return "Device Firmware"; if (device_kind == FU_UEFI_DEVICE_KIND_UEFI_DRIVER) return "UEFI Driver"; if (device_kind == FU_UEFI_DEVICE_KIND_FMP) return "Firmware Management Protocol"; return NULL; } static gchar * fu_plugin_uefi_capsule_get_name_for_type(FuPlugin *plugin, FuUefiDeviceKind device_kind) { GString *display_name; /* set Display Name prefix for capsules that are not PCI cards */ display_name = g_string_new(fu_plugin_uefi_capsule_uefi_type_to_string(device_kind)); if (device_kind == FU_UEFI_DEVICE_KIND_DEVICE_FIRMWARE) g_string_prepend(display_name, "UEFI "); return g_string_free(display_name, FALSE); } static gboolean fu_plugin_uefi_capsule_coldplug_device(FuPlugin *plugin, FuUefiDevice *dev, GError **error) { FuContext *ctx = fu_plugin_get_context(plugin); FuUefiDeviceKind device_kind; /* probe to get add GUIDs (and hence any quirk fixups) */ if (!fu_device_probe(FU_DEVICE(dev), error)) return FALSE; if (!fu_device_setup(FU_DEVICE(dev), error)) return FALSE; /* if not already set by quirks */ if (fu_plugin_has_custom_flag(plugin, "use-legacy-bootmgr-desc")) { fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC); } if (fu_plugin_has_custom_flag(plugin, "supports-boot-order-lock")) { fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK); } if (fu_plugin_has_custom_flag(plugin, "no-ux-capsule")) fu_device_add_private_flag(FU_DEVICE(dev), FU_UEFI_DEVICE_FLAG_NO_UX_CAPSULE); /* set fallback name if nothing else is set */ device_kind = fu_uefi_device_get_kind(dev); if (fu_device_get_name(FU_DEVICE(dev)) == NULL) { g_autofree gchar *name = NULL; name = fu_plugin_uefi_capsule_get_name_for_type(plugin, device_kind); if (name != NULL) fu_device_set_name(FU_DEVICE(dev), name); if (device_kind != FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { fu_device_add_internal_flag(FU_DEVICE(dev), FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY); } } /* set fallback vendor if nothing else is set */ if (fu_device_get_vendor(FU_DEVICE(dev)) == NULL && device_kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) { const gchar *vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER); if (vendor != NULL) fu_device_set_vendor(FU_DEVICE(dev), vendor); } /* set vendor ID as the BIOS vendor */ if (device_kind != FU_UEFI_DEVICE_KIND_FMP) { const gchar *dmi_vendor; dmi_vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VENDOR); if (dmi_vendor != NULL) { g_autofree gchar *vendor_id = g_strdup_printf("DMI:%s", dmi_vendor); fu_device_add_vendor_id(FU_DEVICE(dev), vendor_id); } } /* success */ return TRUE; } static void fu_plugin_uefi_capsule_test_secure_boot(FuPlugin *plugin) { const gchar *result_str = "Disabled"; if (fu_efivar_secure_boot_enabled()) result_str = "Enabled"; fu_plugin_add_report_metadata(plugin, "SecureBoot", result_str); } gboolean fu_plugin_startup(FuPlugin *plugin, GError **error) { FuPluginData *data = fu_plugin_get_data(plugin); guint64 nvram_total; g_autofree gchar *esp_path = NULL; g_autofree gchar *nvram_total_str = NULL; g_autoptr(GError) error_local = NULL; /* don't let user's environment influence test suite failures */ if (g_getenv("FWUPD_UEFI_TEST") != NULL) return TRUE; /* for the uploaded report */ if (fu_plugin_has_custom_flag(plugin, "use-legacy-bootmgr-desc")) fu_plugin_add_report_metadata(plugin, "BootMgrDesc", "legacy"); /* some platforms have broken SMBIOS data */ if (fu_plugin_has_custom_flag(plugin, "uefi-force-enable")) return TRUE; /* use GRUB to load updates */ if (fu_plugin_get_config_value_boolean(plugin, "EnableGrubChainLoad")) { fu_uefi_backend_set_device_gtype(FU_UEFI_BACKEND(data->backend), FU_TYPE_UEFI_GRUB_DEVICE); } /* check we can use this backend */ if (!fu_backend_setup(data->backend, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_WRITE)) { fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED); fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); } g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } /* are the EFI dirs set up so we can update each device */ if (!fu_efivar_supported(error)) return FALSE; nvram_total = fu_efivar_space_used(error); if (nvram_total == G_MAXUINT64) return FALSE; nvram_total_str = g_strdup_printf("%" G_GUINT64_FORMAT, nvram_total); fu_plugin_add_report_metadata(plugin, "EfivarNvramUsed", nvram_total_str); /* override the default ESP path */ esp_path = fu_plugin_get_config_value(plugin, "OverrideESPMountPoint"); if (esp_path != NULL) { data->esp = fu_common_get_esp_for_path(esp_path, error); if (data->esp == NULL) { g_prefix_error(error, "invalid OverrideESPMountPoint=%s " "specified in config: ", esp_path); return FALSE; } } /* test for invalid ESP in coldplug, and set the update-error rather * than showing no output if the plugin had self-disabled here */ return TRUE; } gboolean fu_plugin_unlock(FuPlugin *plugin, FuDevice *device, GError **error) { FuUefiDevice *device_uefi = FU_UEFI_DEVICE(device); FuDevice *device_alt = NULL; FwupdDeviceFlags device_flags_alt = 0; guint flashes_left = 0; guint flashes_left_alt = 0; if (fu_uefi_device_get_kind(device_uefi) != FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Unable to unlock %s", fu_device_get_name(device)); return FALSE; } /* for unlocking TPM1.2 <-> TPM2.0 switching */ g_debug("Unlocking upgrades for: %s (%s)", fu_device_get_name(device), fu_device_get_id(device)); device_alt = fu_device_get_alternate(device); if (device_alt == NULL) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No alternate device for %s", fu_device_get_name(device)); return FALSE; } g_debug("Preventing upgrades for: %s (%s)", fu_device_get_name(device_alt), fu_device_get_id(device_alt)); flashes_left = fu_device_get_flashes_left(device); flashes_left_alt = fu_device_get_flashes_left(device_alt); if (flashes_left == 0) { /* flashes left == 0 on both means no flashes left */ if (flashes_left_alt == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "ERROR: %s has no flashes left.", fu_device_get_name(device)); /* flashes left == 0 on just unlocking device is ownership */ } else { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "ERROR: %s is currently OWNED. " "Ownership must be removed to switch modes.", fu_device_get_name(device_alt)); } return FALSE; } /* clone the info from real device but prevent it from being flashed */ device_flags_alt = fu_device_get_flags(device_alt); fu_device_set_flags(device, device_flags_alt); fu_device_remove_flag(device_alt, FWUPD_DEVICE_FLAG_UPDATABLE); /* make sure that this unlocked device can be updated */ fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); fu_device_set_version(device, "0.0.0.0"); return TRUE; } static void fu_plugin_uefi_update_state_notify_cb(GObject *object, GParamSpec *pspec, FuPlugin *plugin) { FuDevice *device = FU_DEVICE(object); GPtrArray *devices; g_autofree gchar *msg = NULL; /* device is not in needs-reboot state */ if (fu_device_get_update_state(device) != FWUPD_UPDATE_STATE_NEEDS_REBOOT) return; /* only do this on hardware that cannot coalesce multiple capsules */ if (!fu_plugin_has_custom_flag(plugin, "no-coalesce")) return; /* mark every other device for this plugin as non-updatable */ msg = g_strdup_printf("Cannot update as %s [%s] needs reboot", fu_device_get_name(device), fu_device_get_id(device)); devices = fu_plugin_get_devices(plugin); for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); if (device_tmp == device) continue; fu_device_inhibit(device_tmp, "no-coalesce", msg); } } 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) { FuContext *ctx = fu_plugin_get_context(plugin); FuPluginData *data = fu_plugin_get_data(plugin); const gchar *str; g_autoptr(GError) error_udisks2 = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) devices = NULL; if (data->esp == NULL) { data->esp = fu_common_get_esp_default(&error_udisks2); if (data->esp == NULL) { fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND); fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE); fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); g_warning("cannot find default ESP: %s", error_udisks2->message); } } /* 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; devices = fu_backend_get_devices(data->backend); for (guint i = 0; i < devices->len; i++) { FuUefiDevice *dev = g_ptr_array_index(devices, i); fu_device_set_context(FU_DEVICE(dev), ctx); if (data->esp != NULL) fu_uefi_device_set_esp(dev, data->esp); if (!fu_plugin_uefi_capsule_coldplug_device(plugin, dev, error)) return FALSE; fu_device_add_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); /* load all configuration variables */ fu_plugin_uefi_capsule_load_config(plugin, FU_DEVICE(dev)); /* watch in case we set needs-reboot in the engine */ g_signal_connect(dev, "notify::update-state", G_CALLBACK(fu_plugin_uefi_update_state_notify_cb), plugin); fu_plugin_device_add(plugin, FU_DEVICE(dev)); } /* for debugging problems later */ fu_plugin_uefi_capsule_test_secure_boot(plugin); if (!fu_uefi_bgrt_setup(data->bgrt, &error_local)) g_debug("BGRT setup failed: %s", error_local->message); str = fu_uefi_bgrt_get_supported(data->bgrt) ? "Enabled" : "Disabled"; g_debug("UX Capsule support : %s", str); fu_plugin_add_report_metadata(plugin, "UEFIUXCapsule", str); return TRUE; }