mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-06 08:01:26 +00:00

Shim has had a hard time with loading updates from arguments in the most recent release and this isn't the first time that happened. Give distros and users an escape hatch that will allow using GRUB instead.
200 lines
5.7 KiB
C
200 lines
5.7 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,
|
|
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_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;
|
|
}
|