diff --git a/plugins/cros-ec/fu-cros-ec-firmware.c b/plugins/cros-ec/fu-cros-ec-firmware.c new file mode 100644 index 000000000..a4934ac4a --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-firmware.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-fmap-firmware.h" +#include "fu-cros-ec-common.h" +#include "fu-cros-ec-firmware.h" + +#define MAXSECTIONS 2 + +struct _FuCrosEcFirmware { + FuFmapFirmware parent_instance; + struct cros_ec_version version; + FuCrosEcFirmwareSection sections[MAXSECTIONS]; +}; + +G_DEFINE_TYPE (FuCrosEcFirmware, fu_cros_ec_firmware, FU_TYPE_FMAP_FIRMWARE) + +static gboolean +fu_cros_ec_firmware_parse (FuFirmware *firmware, + GBytes *fw, + guint64 addr_start, + guint64 addr_end, + FwupdInstallFlags flags, + GError **error) +{ + FuCrosEcFirmware *self = FU_CROS_EC_FIRMWARE (firmware); + FuFirmware *fmap_firmware = FU_FIRMWARE (firmware); + + self->sections[0].name = "RO"; + self->sections[1].name = "RW"; + + for (gsize i = 0; i < G_N_ELEMENTS (self->sections); i++) { + gboolean rw = FALSE; + FuCrosEcFirmwareSection *section = &self->sections[i]; + const gchar *fmap_name; + const gchar *fmap_fwid_name; + g_autoptr(FuFirmwareImage) img = NULL; + g_autoptr(FuFirmwareImage) fwid_img = NULL; + g_autoptr(GBytes) payload_bytes = NULL; + g_autoptr(GBytes) fwid_bytes = NULL; + + if (g_strcmp0 (section->name, "RO") == 0) { + fmap_name = "EC_RO"; + fmap_fwid_name = "RO_FRID"; + } else if (g_strcmp0 (section->name, "RW") == 0) { + rw = TRUE; + fmap_name = "EC_RW"; + fmap_fwid_name = "RW_FWID"; + } else { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "incorrect section name"); + return FALSE; + } + + img = fu_firmware_get_image_by_id (fmap_firmware, + fmap_name, error); + if (img == NULL) { + g_prefix_error (error, "%s image not found: ", + fmap_name); + return FALSE; + } + + fwid_img = fu_firmware_get_image_by_id (fmap_firmware, + fmap_fwid_name, error); + if (fwid_img == NULL) { + g_prefix_error (error, "%s image not found: ", + fmap_fwid_name); + return FALSE; + } + fwid_bytes = fu_firmware_image_write (fwid_img, error); + if (fwid_bytes == NULL) { + g_prefix_error (error, + "unable to get bytes from %s: ", + fmap_fwid_name); + return FALSE; + } + if (!fu_memcpy_safe ((guint8 *) section->version, + FMAP_STRLEN, 0x0, + g_bytes_get_data (fwid_bytes, NULL), + g_bytes_get_size (fwid_bytes), 0x0, + g_bytes_get_size (fwid_bytes), error)) + return FALSE; + + payload_bytes = fu_firmware_image_write (img, error); + if (payload_bytes == NULL) { + g_prefix_error (error, + "unable to get bytes from %s: ", + fmap_name); + return FALSE; + } + section->offset = fu_firmware_image_get_addr (img); + section->size = g_bytes_get_size (payload_bytes); + fu_firmware_image_set_version (img, section->version); + + if (rw) { + if (!fu_cros_ec_parse_version (section->version, + &self->version, + error)) { + g_prefix_error (error, + "failed parsing firmware's version: %32s: ", + section->version); + return FALSE; + } + fu_firmware_set_version (firmware, + self->version.triplet); + } + } + + /* success */ + return TRUE; +} + +static void +fu_cros_ec_firmware_init (FuCrosEcFirmware *self) +{ +} + +static void +fu_cros_ec_firmware_class_init (FuCrosEcFirmwareClass *klass) +{ + FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_CLASS (klass); + klass_firmware->parse = fu_cros_ec_firmware_parse; +} + +FuFirmware * +fu_cros_ec_firmware_new (void) +{ + return g_object_new (FU_TYPE_CROS_EC_FIRMWARE, NULL); +} diff --git a/plugins/cros-ec/fu-cros-ec-firmware.h b/plugins/cros-ec/fu-cros-ec-firmware.h new file mode 100644 index 000000000..54c975320 --- /dev/null +++ b/plugins/cros-ec/fu-cros-ec-firmware.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Benson Leung + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include "fu-firmware.h" +#include "fu-fmap-firmware.h" + +#define FU_TYPE_CROS_EC_FIRMWARE (fu_cros_ec_firmware_get_type ()) +G_DECLARE_FINAL_TYPE (FuCrosEcFirmware, fu_cros_ec_firmware, FU, CROS_EC_FIRMWARE, FuFmapFirmware) + +/* + * Each RO or RW section of the new image can be in one of the following + * states. + */ +typedef enum { + FU_CROS_EC_FW_NOT_NEEDED= 0, /* Version below or equal that on the target. */ + FU_CROS_EC_FW_NOT_POSSIBLE, /* + * RO is newer, but can't be transferred due to + * target RW shortcomings. + */ + FU_CROS_EC_FW_NEEDED /* + * This section needs to be transferred to the + * target. + */ +} FuCrosEcFirmwareUpgradeStatus; + +typedef struct { + const gchar *name; + guint32 offset; + gsize size; + FuCrosEcFirmwareUpgradeStatus ustatus; + gchar version[FMAP_STRLEN]; + gint32 rollback; + guint32 key_version; +} FuCrosEcFirmwareSection; + +FuFirmware *fu_cros_ec_firmware_new (void); diff --git a/plugins/cros-ec/fu-plugin-cros-ec.c b/plugins/cros-ec/fu-plugin-cros-ec.c index cd55e8652..5c1f430c1 100644 --- a/plugins/cros-ec/fu-plugin-cros-ec.c +++ b/plugins/cros-ec/fu-plugin-cros-ec.c @@ -10,10 +10,12 @@ #include "fu-hash.h" #include "fu-cros-ec-usb-device.h" +#include "fu-cros-ec-firmware.h" void fu_plugin_init (FuPlugin *plugin) { fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); fu_plugin_set_device_gtype (plugin, FU_TYPE_CROS_EC_USB_DEVICE); + fu_plugin_add_firmware_gtype (plugin, "cros-ec", FU_TYPE_CROS_EC_FIRMWARE); } diff --git a/plugins/cros-ec/meson.build b/plugins/cros-ec/meson.build index ff41c03d9..643aa9d1e 100644 --- a/plugins/cros-ec/meson.build +++ b/plugins/cros-ec/meson.build @@ -10,6 +10,7 @@ shared_module('fu_plugin_cros_ec', 'fu-plugin-cros-ec.c', 'fu-cros-ec-usb-device.c', 'fu-cros-ec-common.c', + 'fu-cros-ec-firmware.c', ], include_directories : [ root_incdir,