uefi: Append the header on capsules without headers from Linux

This allows using better heuristics and potentially phasing this out
in the future.
This commit is contained in:
Mario Limonciello 2018-12-10 14:58:22 -06:00 committed by Mario Limonciello
parent 6131f5df8f
commit 2ac3aca2a7
5 changed files with 80 additions and 64 deletions

View File

@ -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);
}

View File

@ -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)) {

View File

@ -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) {

View File

@ -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 */

View File

@ -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