mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-02 18:30:40 +00:00
191 lines
4.9 KiB
C
191 lines
4.9 KiB
C
/*
|
|
* Copyright (C) 2020 Benson Leung <bleung@chromium.org>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupdplugin.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;
|
|
GPtrArray *sections;
|
|
};
|
|
|
|
G_DEFINE_TYPE(FuCrosEcFirmware, fu_cros_ec_firmware, FU_TYPE_FMAP_FIRMWARE)
|
|
|
|
gboolean
|
|
fu_cros_ec_firmware_pick_sections(FuCrosEcFirmware *self, guint32 writeable_offset, GError **error)
|
|
{
|
|
gboolean found = FALSE;
|
|
|
|
for (gsize i = 0; i < self->sections->len; i++) {
|
|
FuCrosEcFirmwareSection *section = g_ptr_array_index(self->sections, i);
|
|
guint32 offset = section->offset;
|
|
|
|
if (offset != writeable_offset)
|
|
continue;
|
|
|
|
section->ustatus = FU_CROS_EC_FW_NEEDED;
|
|
found = TRUE;
|
|
}
|
|
|
|
if (!found) {
|
|
g_set_error(error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_DATA,
|
|
"no writeable section found with offset: 0x%x",
|
|
writeable_offset);
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
GPtrArray *
|
|
fu_cros_ec_firmware_get_sections(FuCrosEcFirmware *self)
|
|
{
|
|
return self->sections;
|
|
}
|
|
|
|
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);
|
|
|
|
for (gsize i = 0; i < self->sections->len; i++) {
|
|
gboolean rw = FALSE;
|
|
FuCrosEcFirmwareSection *section = g_ptr_array_index(self->sections, i);
|
|
const gchar *fmap_name;
|
|
const gchar *fmap_fwid_name;
|
|
g_autoptr(FuFirmware) img = NULL;
|
|
g_autoptr(FuFirmware) 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_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->raw_version,
|
|
FU_FMAP_FIRMWARE_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_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_get_addr(img);
|
|
section->size = g_bytes_get_size(payload_bytes);
|
|
fu_firmware_set_version(img, section->raw_version);
|
|
section->image_idx = fu_firmware_get_idx(img);
|
|
|
|
if (!fu_cros_ec_parse_version(section->raw_version, §ion->version, error)) {
|
|
g_prefix_error(error,
|
|
"failed parsing firmware's version: %32s: ",
|
|
section->raw_version);
|
|
return FALSE;
|
|
}
|
|
|
|
if (rw) {
|
|
if (!fu_cros_ec_parse_version(section->raw_version,
|
|
&self->version,
|
|
error)) {
|
|
g_prefix_error(error,
|
|
"failed parsing firmware's version: %32s: ",
|
|
section->raw_version);
|
|
return FALSE;
|
|
}
|
|
fu_firmware_set_version(firmware, self->version.triplet);
|
|
}
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
fu_cros_ec_firmware_init(FuCrosEcFirmware *self)
|
|
{
|
|
FuCrosEcFirmwareSection *section;
|
|
|
|
self->sections = g_ptr_array_new_with_free_func(g_free);
|
|
section = g_new0(FuCrosEcFirmwareSection, 1);
|
|
section->name = "RO";
|
|
g_ptr_array_add(self->sections, section);
|
|
section = g_new0(FuCrosEcFirmwareSection, 1);
|
|
section->name = "RW";
|
|
g_ptr_array_add(self->sections, section);
|
|
}
|
|
|
|
static void
|
|
fu_cros_ec_firmware_finalize(GObject *object)
|
|
{
|
|
FuCrosEcFirmware *self = FU_CROS_EC_FIRMWARE(object);
|
|
g_ptr_array_free(self->sections, TRUE);
|
|
G_OBJECT_CLASS(fu_cros_ec_firmware_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
fu_cros_ec_firmware_class_init(FuCrosEcFirmwareClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
FuFmapFirmwareClass *klass_firmware = FU_FMAP_FIRMWARE_CLASS(klass);
|
|
klass_firmware->parse = fu_cros_ec_firmware_parse;
|
|
object_class->finalize = fu_cros_ec_firmware_finalize;
|
|
}
|
|
|
|
FuFirmware *
|
|
fu_cros_ec_firmware_new(void)
|
|
{
|
|
return g_object_new(FU_TYPE_CROS_EC_FIRMWARE, NULL);
|
|
}
|