fwupd/plugins/uefi-capsule/fu-uefi-grub-device.c
Richard Hughes 40cd18fa97 Allow using a per-device global percentage completion
It's actually quite hard to build a front-end for fwupd at the moment
as you're never sure when the progress bar is going to zip back to 0%
and start all over again. Some plugins go 0..100% for write, others
go 0..100% for erase, then again for write, then *again* for verify.

By creating a helper object we can easily split up the progress of the
specific task, e.g. write_firmware().

We can encode at the plugin level "the erase takes 50% of the time, the
write takes 40% and the read takes 10%". This means we can have a
progressbar which goes up just once at a consistent speed.
2021-09-13 14:28:15 +01:00

210 lines
6.1 KiB
C

/*
* Copyright (C) 2021 Mario Limonciello <mario.limonciello@amd.com>
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-uefi-common.h"
#include "fu-uefi-grub-device.h"
struct _FuUefiGrubDevice {
FuUefiDevice parent_instance;
};
G_DEFINE_TYPE(FuUefiGrubDevice, fu_uefi_grub_device, FU_TYPE_UEFI_DEVICE)
static gboolean
fu_uefi_grub_device_mkconfig(FuDevice *device,
const gchar *esp_path,
const gchar *target_app,
GError **error)
{
const gchar *argv_mkconfig[] = {"", "-o", "/boot/grub/grub.cfg", NULL};
const gchar *argv_reboot[] = {"", "fwupd", NULL};
g_autofree gchar *grub_mkconfig = NULL;
g_autofree gchar *grub_reboot = NULL;
g_autofree gchar *grub_target = NULL;
g_autofree gchar *localstatedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR_PKG);
g_autofree gchar *output = NULL;
g_autoptr(GString) str = g_string_new(NULL);
/* find grub.conf */
if (!g_file_test(argv_mkconfig[2], G_FILE_TEST_EXISTS))
argv_mkconfig[2] = "/boot/grub2/grub.cfg";
if (!g_file_test(argv_mkconfig[2], G_FILE_TEST_EXISTS)) {
g_set_error_literal(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"could not find grub.conf");
return FALSE;
}
/* find grub-mkconfig */
grub_mkconfig = fu_common_find_program_in_path("grub-mkconfig", NULL);
if (grub_mkconfig == NULL)
grub_mkconfig = fu_common_find_program_in_path("grub2-mkconfig", NULL);
if (grub_mkconfig == NULL) {
g_set_error_literal(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"could not find grub-mkconfig");
return FALSE;
}
/* find grub-reboot */
grub_reboot = fu_common_find_program_in_path("grub-reboot", NULL);
if (grub_reboot == NULL)
grub_reboot = fu_common_find_program_in_path("grub2-reboot", NULL);
if (grub_reboot == NULL) {
g_set_error_literal(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"could not find grub-reboot");
return FALSE;
}
/* replace ESP info in conf with what we detected */
g_string_append_printf(str, "EFI_PATH=%s\n", target_app);
fu_common_string_replace(str, esp_path, "");
g_string_append_printf(str, "ESP=%s\n", esp_path);
grub_target = g_build_filename(localstatedir, "uefi_capsule.conf", NULL);
if (!g_file_set_contents(grub_target, str->str, -1, error))
return FALSE;
/* refresh GRUB configuration */
argv_mkconfig[0] = grub_mkconfig;
if (!g_spawn_sync(NULL,
(gchar **)argv_mkconfig,
NULL,
G_SPAWN_DEFAULT,
NULL,
NULL,
&output,
NULL,
NULL,
error))
return FALSE;
if (g_getenv("FWUPD_UPDATE_CAPSULE_VERBOSE") != NULL)
g_debug("%s", output);
/* make fwupd default */
argv_reboot[0] = grub_reboot;
return g_spawn_sync(NULL,
(gchar **)argv_reboot,
NULL,
G_SPAWN_DEFAULT,
NULL,
NULL,
NULL,
NULL,
NULL,
error);
}
static gboolean
fu_uefi_grub_device_write_firmware(FuDevice *device,
FuFirmware *firmware,
FuProgress *progress,
FwupdInstallFlags flags,
GError **error)
{
FuUefiDevice *self = FU_UEFI_DEVICE(device);
const gchar *fw_class = fu_uefi_device_get_guid(self);
g_autofree gchar *basename = NULL;
g_autofree gchar *directory = NULL;
g_autofree gchar *esp_path = fu_uefi_device_get_esp_path(self);
g_autofree gchar *fn = NULL;
g_autofree gchar *source_app = NULL;
g_autofree gchar *target_app = NULL;
g_autofree gchar *varname = fu_uefi_device_build_varname(self);
g_autoptr(GBytes) fixed_fw = NULL;
g_autoptr(GBytes) fw = NULL;
/* 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;
/* skip for self tests */
if (g_getenv("FWUPD_UEFI_TEST") != NULL)
return TRUE;
/* 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;
/* if secure boot was turned on this might need to be installed separately */
source_app = fu_uefi_get_built_app_path(error);
if (source_app == NULL)
return FALSE;
/* test if correct asset in place */
target_app = fu_uefi_get_esp_app_path(device, esp_path, "fwupd", error);
if (target_app == NULL)
return FALSE;
if (!fu_uefi_cmp_asset(source_app, target_app)) {
if (!fu_uefi_copy_asset(source_app, target_app, error))
return FALSE;
}
/* we are using GRUB instead of NVRAM variables */
return fu_uefi_grub_device_mkconfig(device, esp_path, target_app, error);
}
static void
fu_uefi_grub_device_report_metadata_pre(FuDevice *device, GHashTable *metadata)
{
/* FuUefiDevice */
FU_DEVICE_CLASS(fu_uefi_grub_device_parent_class)->report_metadata_pre(device, metadata);
g_hash_table_insert(metadata, g_strdup("CapsuleApplyMethod"), g_strdup("grub"));
}
static void
fu_uefi_grub_device_init(FuUefiGrubDevice *self)
{
}
static void
fu_uefi_grub_device_class_init(FuUefiGrubDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
klass_device->write_firmware = fu_uefi_grub_device_write_firmware;
klass_device->report_metadata_pre = fu_uefi_grub_device_report_metadata_pre;
}