fwupd/plugins/flashrom/fu-flashrom-internal-device.c
Sean Rhodes 2e856d21e0 plugins/flashrom: Only backup BIOS region
As we are only writing to the BIOS region, we only need to backup the BIOS
region. The will avoid the error "failed to back up original firmware" if
regions such as the ME can't be read.
2021-09-03 12:04:20 +01:00

176 lines
5.3 KiB
C

/*
* Copyright (C) 2021 Daniel Campello <campello@chromium.org>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <libflashrom.h>
#include "fu-flashrom-device.h"
#include "fu-flashrom-internal-device.h"
struct _FuFlashromInternalDevice {
FuFlashromDevice parent_instance;
};
G_DEFINE_TYPE(FuFlashromInternalDevice, fu_flashrom_internal_device, FU_TYPE_FLASHROM_DEVICE)
static void
fu_flashrom_internal_device_init(FuFlashromInternalDevice *self)
{
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL);
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC);
fu_device_add_instance_id(FU_DEVICE(self), "main-system-firmware");
fu_device_add_internal_flag(FU_DEVICE(self), FU_DEVICE_INTERNAL_FLAG_ENSURE_SEMVER);
fu_device_set_physical_id(FU_DEVICE(self), "flashrom");
fu_device_set_logical_id(FU_DEVICE(self), "bios");
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_add_icon(FU_DEVICE(self), "computer");
}
static gboolean
fu_flashrom_internal_device_prepare(FuDevice *device, FwupdInstallFlags flags, GError **error)
{
g_autofree gchar *firmware_orig = NULL;
g_autofree gchar *localstatedir = NULL;
g_autofree gchar *basename = NULL;
/* if the original firmware doesn't exist, grab it now */
basename = g_strdup_printf("flashrom-%s.bin", fu_device_get_id(device));
localstatedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR_PKG);
firmware_orig = g_build_filename(localstatedir, "builder", basename, NULL);
if (!fu_common_mkdir_parent(firmware_orig, error))
return FALSE;
if (!g_file_test(firmware_orig, G_FILE_TEST_EXISTS)) {
FuFlashromDevice *parent = FU_FLASHROM_DEVICE(device);
struct flashrom_flashctx *flashctx = fu_flashrom_device_get_flashctx(parent);
gsize flash_size = fu_flashrom_device_get_flash_size(parent);
struct flashrom_layout *layout;
g_autofree guint8 *newcontents = g_malloc0(flash_size);
g_autoptr(GBytes) buf = NULL;
fu_device_set_status(device, FWUPD_STATUS_DEVICE_READ);
if (flashrom_layout_read_from_ifd(&layout, flashctx, NULL, 0)) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"failed to read layout from Intel ICH descriptor");
return FALSE;
}
/* include bios region for safety reasons */
if (flashrom_layout_include_region(layout, "bios")) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"invalid region name");
return FALSE;
}
/* read region */
flashrom_layout_set(flashctx, layout);
if (flashrom_image_read(flashctx, newcontents, flash_size)) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"failed to back up original firmware");
return FALSE;
}
buf = g_bytes_new_static(newcontents, flash_size);
if (!fu_common_set_contents_bytes(firmware_orig, buf, error))
return FALSE;
}
return TRUE;
}
static gboolean
fu_flashrom_internal_device_write_firmware(FuDevice *device,
FuFirmware *firmware,
FwupdInstallFlags flags,
GError **error)
{
FuFlashromDevice *parent = FU_FLASHROM_DEVICE(device);
struct flashrom_flashctx *flashctx = fu_flashrom_device_get_flashctx(parent);
gsize flash_size = fu_flashrom_device_get_flash_size(parent);
struct flashrom_layout *layout;
gsize sz = 0;
gint rc;
const guint8 *buf;
g_autoptr(GBytes) blob_fw = fu_firmware_get_bytes(firmware, error);
if (blob_fw == NULL)
return FALSE;
buf = g_bytes_get_data(blob_fw, &sz);
if (flashrom_layout_read_from_ifd(&layout, flashctx, NULL, 0)) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_READ,
"failed to read layout from Intel ICH descriptor");
return FALSE;
}
/* include bios region for safety reasons */
if (flashrom_layout_include_region(layout, "bios")) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"invalid region name");
return FALSE;
}
/* write region */
flashrom_layout_set(flashctx, layout);
if (sz != flash_size) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"invalid image size 0x%x, expected 0x%x",
(guint)sz,
(guint)flash_size);
return FALSE;
}
fu_device_set_status(device, FWUPD_STATUS_DEVICE_WRITE);
fu_device_set_progress(device, 0); /* urgh */
rc = flashrom_image_write(flashctx, (void *)buf, sz, NULL /* refbuffer */);
if (rc != 0) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"image write failed, err=%i",
rc);
return FALSE;
}
fu_device_set_status(device, FWUPD_STATUS_DEVICE_VERIFY);
if (flashrom_image_verify(flashctx, (void *)buf, sz)) {
g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "image verify failed");
return FALSE;
}
flashrom_layout_release(layout);
/* success */
return TRUE;
}
static void
fu_flashrom_internal_device_class_init(FuFlashromInternalDeviceClass *klass)
{
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
klass_device->prepare = fu_flashrom_internal_device_prepare;
klass_device->write_firmware = fu_flashrom_internal_device_write_firmware;
}
FuDevice *
fu_flashrom_internal_device_new(void)
{
return FU_DEVICE(g_object_new(FU_TYPE_FLASHROM_INTERNAL_DEVICE, NULL));
}