mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-25 22:40:50 +00:00

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.
210 lines
6.1 KiB
C
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;
|
|
}
|