From 2ac3aca2a70a2ab2714cbeeac576d444be442209 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Dec 2018 14:58:22 -0600 Subject: [PATCH] uefi: Append the header on capsules without headers from Linux This allows using better heuristics and potentially phasing this out in the future. --- plugins/dell/fu-self-test.c | 5 ++- plugins/uefi/efi/fwupdate.c | 70 ++++------------------------------- plugins/uefi/fu-plugin-uefi.c | 4 ++ plugins/uefi/fu-uefi-device.c | 64 +++++++++++++++++++++++++++++++- plugins/uefi/fu-uefi-device.h | 1 + 5 files changed, 80 insertions(+), 64 deletions(-) diff --git a/plugins/dell/fu-self-test.c b/plugins/dell/fu-self-test.c index ff07c4bcd..083b0b683 100644 --- a/plugins/dell/fu-self-test.c +++ b/plugins/dell/fu-self-test.c @@ -69,7 +69,7 @@ fu_plugin_dell_tpm_func (void) struct tpm_status tpm_out; g_autoptr(FuPlugin) plugin_dell = NULL; g_autoptr(FuPlugin) plugin_uefi = NULL; - g_autoptr(GBytes) blob_fw = g_bytes_new_static ("fw", 2); + g_autoptr(GBytes) blob_fw = g_bytes_new_static ("fw", 30); g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; @@ -244,8 +244,11 @@ fu_plugin_dell_tpm_func (void) g_clear_error (&error); /* test override */ + g_test_expect_message ("FuPluginUefi", G_LOG_LEVEL_WARNING, + "missing or invalid embedded capsule header"); ret = fu_plugin_runner_update (plugin_uefi, device_v20, NULL, blob_fw, FWUPD_INSTALL_FLAG_FORCE, &error); + g_test_assert_expected_messages (); g_assert_no_error (error); g_assert (ret); } diff --git a/plugins/uefi/efi/fwupdate.c b/plugins/uefi/efi/fwupdate.c index 3cbe16f6d..ecf621946 100644 --- a/plugins/uefi/efi/fwupdate.c +++ b/plugins/uefi/efi/fwupdate.c @@ -19,8 +19,6 @@ EFI_GUID fwupdate_guid = {0x0abba7dc,0xe516,0x4167,{0xbb,0xf5,0x4d,0x9d,0x1c,0x73,0x94,0x16}}; EFI_GUID ux_capsule_guid = {0x3b8c8162,0x188c,0x46a4,{0xae,0xc9,0xbe,0x43,0xf1,0xd6,0x56,0x97}}; -EFI_GUID fmp_capsule_guid = - {0x6dcbd5ed,0xe82d,0x4c44,{0xbd,0xa1,0x71,0x94,0x19,0x9a,0xd9,0x2a}}; EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE; @@ -1017,7 +1015,6 @@ do_ux_csum(EFI_HANDLE loaded_image, UINT8 *buf, UINTN size) } #define is_ux_capsule(guid) (guid_cmp(guid, &ux_capsule_guid) == 0) -#define is_fmp_capsule(guid) (guid_cmp(guid, &fmp_capsule_guid) == 0) static EFI_STATUS add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, @@ -1047,68 +1044,17 @@ add_capsule(update_table *update, EFI_CAPSULE_HEADER **capsule_out, dprint(L"updates guid: %g\n", &update->info->guid); dprint(L"File guid: %g\n", fbuf); - /* - * See if it has the capsule header, and if not, add one. - * - * Unfortunately there's not a good way to do this, so we're just - * checking if the capsule has the fw_class guid at the right place. - */ - if ((guid_cmp(&update->info->guid, (efi_guid_t *)fbuf) == 0 || - is_fmp_capsule((efi_guid_t *)fbuf)) && - /* - * We're ignoring things that are 40 bytes here, because that's - * the size of the variables used in the test code I wrote for - * edk2 - It's basically a capsule header with no payload, so - * there's nothing real it can do anyway. - * - * At some point I'll update that to be slightly different and - * take the exception out, but it's not pressing. - */ - fsize != 40) { - dprint(L"Image has capsule image embedded\n"); - cbd_len = fsize; - cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf; - capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf; - if (!cap_out->Flags && !is_ux_capsule(&update->info->guid)) { + cbd_len = fsize; + cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)fbuf; + capsule = cap_out = (EFI_CAPSULE_HEADER *)fbuf; + if (!cap_out->Flags && !is_ux_capsule(&update->info->guid)) { #if defined(__aarch64__) - cap_out->Flags |= update->info->capsule_flags; + cap_out->Flags |= update->info->capsule_flags; #else - cap_out->Flags |= update->info->capsule_flags | - CAPSULE_FLAGS_PERSIST_ACROSS_RESET | - CAPSULE_FLAGS_INITIATE_RESET; + cap_out->Flags |= update->info->capsule_flags | + CAPSULE_FLAGS_PERSIST_ACROSS_RESET | + CAPSULE_FLAGS_INITIATE_RESET; #endif - } - } else { - dprint(L"Image does not have embedded header\n"); - dprint(L"Allocating %d for capsule header.\n", - sizeof (*capsule)+fsize); - rc = allocate((void **)&capsule, sizeof (*capsule) + fsize); - if (EFI_ERROR(rc)) { - print(L"Tried to allocate %d\n", - sizeof (*capsule) + fsize); - print(L"Could not allocate space for update: %r.\n", - rc); - return EFI_OUT_OF_RESOURCES; - } - capsule->CapsuleGuid = update->info->guid; - capsule->HeaderSize = sizeof (*capsule); - if (!is_ux_capsule(&update->info->guid)) { -#if defined(__aarch64__) - capsule->Flags |= update->info->capsule_flags; -#else - capsule->Flags = update->info->capsule_flags | - CAPSULE_FLAGS_PERSIST_ACROSS_RESET | - CAPSULE_FLAGS_INITIATE_RESET; -#endif - } - capsule->CapsuleImageSize = fsize + sizeof (*capsule); - - UINT8 *buffer = (UINT8 *)capsule + capsule->HeaderSize; - CopyMem(buffer, fbuf, fsize); - cbd_len = capsule->CapsuleImageSize; - cbd_data = (EFI_PHYSICAL_ADDRESS)(UINTN)capsule; - cap_out = capsule; - free(fbuf, fsize); } if (is_ux_capsule(&update->info->guid)) { diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 8bf396f66..8d2d3728f 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -377,6 +377,10 @@ fu_plugin_update (FuPlugin *plugin, if (!fu_device_write_firmware (device, blob_fw, error)) return FALSE; + /* record if we had an invalid header during update */ + str = fu_uefi_missing_capsule_header (device) ? "True" : "False"; + fu_plugin_add_report_metadata (plugin, "MissingCapsuleHeader", str); + /* record boot information to system log for future debugging */ efibootmgr_path = fu_common_find_program_in_path ("efibootmgr", NULL); if (efibootmgr_path != NULL) { diff --git a/plugins/uefi/fu-uefi-device.c b/plugins/uefi/fu-uefi-device.c index 2e6c22805..aff961a38 100644 --- a/plugins/uefi/fu-uefi-device.c +++ b/plugins/uefi/fu-uefi-device.c @@ -28,6 +28,7 @@ struct _FuUefiDevice { FuUefiDeviceStatus last_attempt_status; guint32 last_attempt_version; guint64 fmp_hardware_instance; + gboolean missing_header; }; G_DEFINE_TYPE (FuUefiDevice, fu_uefi_device, FU_TYPE_DEVICE) @@ -289,6 +290,63 @@ 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) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + gsize fw_length; + efi_guid_t esrt_guid; + efi_guid_t payload_guid; + const gchar *data = g_bytes_get_data (fw, &fw_length); + self->missing_header = FALSE; + + /* convert to EFI GUIDs */ + if (efi_str_to_guid (fu_uefi_device_get_guid (self), &esrt_guid) < 0) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, + "Invalid ESRT GUID"); + return NULL; + } + if (fw_length < sizeof(efi_guid_t)) { + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, + "Invalid payload"); + return NULL; + } + memcpy (&payload_guid, data, sizeof(efi_guid_t)); + + /* ESRT header matches payload */ + if (efi_guid_cmp (&esrt_guid, &payload_guid) == 0) { + g_debug ("ESRT matches payload GUID"); + return g_bytes_new_from_bytes (fw, 0, fw_length); + /* FMP payload */ + } else if (fu_uefi_device_get_kind (self) == FU_UEFI_DEVICE_KIND_FMP) { + g_debug ("performing FMP update"); + return g_bytes_new_from_bytes (fw, 0, fw_length); + /* Missing, add a header */ + } else { + guint header_size = sizeof(efi_capsule_header_t); + guint8 *new_data = g_malloc (fw_length + header_size); + guint8 *capsule = new_data + header_size; + efi_capsule_header_t *header = (efi_capsule_header_t *) new_data; + + g_warning ("missing or invalid embedded capsule header"); + self->missing_header = TRUE; + header->flags = self->capsule_flags; + header->header_size = header_size; + header->capsule_image_size = fw_length + header_size; + memcpy (&header->guid, &esrt_guid, sizeof (efi_guid_t)); + memcpy (capsule, data, fw_length); + + return g_bytes_new_take (new_data, fw_length + header_size); + } +} + +gboolean +fu_uefi_missing_capsule_header (FuDevice *device) +{ + FuUefiDevice *self = FU_UEFI_DEVICE (device); + return self->missing_header; +} + static gboolean fu_uefi_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) { @@ -299,6 +357,7 @@ fu_uefi_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) efi_update_info_t info; gsize datasz = 0; gsize dp_bufsz = 0; + g_autoptr(GBytes) fixed_fw = NULL; g_autofree gchar *basename = NULL; g_autofree gchar *directory = NULL; g_autofree gchar *fn = NULL; @@ -320,7 +379,10 @@ fu_uefi_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) fn = g_build_filename (directory, "fw", basename, NULL); if (!fu_common_mkdir_parent (fn, error)) return FALSE; - if (!fu_common_set_contents_bytes (fn, fw, error)) + 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; /* set the blob header shared with fwupd.efi */ diff --git a/plugins/uefi/fu-uefi-device.h b/plugins/uefi/fu-uefi-device.h index f2df02a90..98aee51b4 100644 --- a/plugins/uefi/fu-uefi-device.h +++ b/plugins/uefi/fu-uefi-device.h @@ -57,6 +57,7 @@ const gchar *fu_uefi_device_kind_to_string (FuUefiDeviceKind kind); const gchar *fu_uefi_device_status_to_string (FuUefiDeviceStatus status); FuUefiUpdateInfo *fu_uefi_device_load_update_info (FuUefiDevice *self, GError **error); +gboolean fu_uefi_missing_capsule_header (FuDevice *device); G_END_DECLS