From 48d9fb8f74ecab495d3e98b0849e4fdabc513f74 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 18 Mar 2020 18:33:08 +0000 Subject: [PATCH] ccgx: Parse the metadata block in the firmware image --- plugins/ccgx/fu-ccgx-common.h | 24 +++++ plugins/ccgx/fu-ccgx-cyacd-firmware-image.c | 101 ++++++++++++++++++++ plugins/ccgx/fu-ccgx-cyacd-firmware-image.h | 2 + plugins/ccgx/fu-ccgx-cyacd-firmware.c | 2 + 4 files changed, 129 insertions(+) create mode 100644 plugins/ccgx/fu-ccgx-common.h diff --git a/plugins/ccgx/fu-ccgx-common.h b/plugins/ccgx/fu-ccgx-common.h new file mode 100644 index 000000000..f4344c360 --- /dev/null +++ b/plugins/ccgx/fu-ccgx-common.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 Cypress Semiconductor Corporation. + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +/* metadata valid signature "CY" */ +#define CCGX_METADATA_VALID_SIG 0x4359 + +typedef struct __attribute__((packed)) { + guint8 fw_checksum; /* firmware checksum */ + guint32 fw_entry; /* firmware entry address */ + guint16 last_boot_row; /* last flash row of bootloader or previous firmware */ + guint8 reserved1[2]; /* reserved */ + guint32 fw_size; /* firmware size */ + guint8 reserved2[9]; /* reserved */ + guint16 metadata_valid; /* meta data valid "CY" */ + guint8 reserved3[4]; /* reserved */ + guint32 boot_seq; /* boot sequence number */ +} CCGxMetaData; diff --git a/plugins/ccgx/fu-ccgx-cyacd-firmware-image.c b/plugins/ccgx/fu-ccgx-cyacd-firmware-image.c index 7930bc7cc..55cdb5e60 100644 --- a/plugins/ccgx/fu-ccgx-cyacd-firmware-image.c +++ b/plugins/ccgx/fu-ccgx-cyacd-firmware-image.c @@ -8,10 +8,15 @@ #include "config.h" #include "fu-common.h" +#include "fu-common-version.h" #include "fu-firmware-common.h" +#include "fu-ccgx-common.h" #include "fu-ccgx-cyacd-firmware-image.h" +/* offset stored appication version for CCGx */ +#define CCGX_APP_VERSION_OFFSET 228 /* 128+64+32+4 */ + struct _FuCcgxCyacdFirmwareImage { FuFirmwareImageClass parent_instance; GPtrArray *records; @@ -36,6 +41,102 @@ fu_ccgx_cyacd_firmware_image_record_free (FuCcgxCyacdFirmwareImageRecord *rcd) G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCcgxCyacdFirmwareImageRecord, fu_ccgx_cyacd_firmware_image_record_free) +gboolean +fu_ccgx_cyacd_firmware_image_parse_md_block (FuCcgxCyacdFirmwareImage *self, GError **error) +{ + FuCcgxCyacdFirmwareImageRecord *rcd; + CCGxMetaData metadata; + const guint8 *buf; + gsize bufsz = 0; + gsize md_offset = 0; + guint32 fw_size = 0; + guint32 rcd_version_idx = 0; + guint32 version = 0; + guint8 checksum_calc = 0; + g_autofree gchar *version_str = NULL; + + /* sanity check */ + if (self->records->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no records added to image"); + return FALSE; + } + + /* read metadata from correct ofsset */ + rcd = g_ptr_array_index (self->records, self->records->len - 1); + buf = g_bytes_get_data (rcd->data, &bufsz); + switch (bufsz) { + case 0x80: + md_offset = 0x40; + break; + case 0x100: + md_offset = 0xC0; + break; + default: + break; + } + if (!fu_memcpy_safe ((guint8 *) &metadata, sizeof(metadata), 0x0, /* dst */ + buf, bufsz, md_offset, sizeof(metadata), error)) /* src */ + return FALSE; + + /* sanity check */ + if (metadata.metadata_valid != CCGX_METADATA_VALID_SIG) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid metadata 0x@%x, expected 0x%04x, got 0x%04x", + (guint) md_offset, + (guint) CCGX_METADATA_VALID_SIG, + (guint) metadata.metadata_valid); + return FALSE; + } + for (guint i = 0; i < self->records->len - 1; i++) { + rcd = g_ptr_array_index (self->records, i); + buf = g_bytes_get_data (rcd->data, &bufsz); + fw_size += bufsz; + for (gsize j = 0; j < bufsz; j++) + checksum_calc += buf[j]; + } + if (fw_size != metadata.fw_size) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware size invalid, got %02x, expected %02x", + fw_size, metadata.fw_size); + return FALSE; + } + checksum_calc = 1 + ~checksum_calc; + if (metadata.fw_checksum != checksum_calc) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum invalid, got %02x, expected %02x", + checksum_calc, metadata.fw_checksum); + return FALSE; + } + + /* get version */ + rcd_version_idx = CCGX_APP_VERSION_OFFSET / bufsz; + if (rcd_version_idx >= self->records->len) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid version index of %02x", + rcd_version_idx); + return FALSE; + } + rcd = g_ptr_array_index (self->records, rcd_version_idx); + buf = g_bytes_get_data (rcd->data, &bufsz); + if (!fu_common_read_uint32_safe (buf, bufsz, CCGX_APP_VERSION_OFFSET % bufsz, + &version, G_LITTLE_ENDIAN, error)) + return FALSE; + version_str = fu_common_version_from_uint32 (version, FWUPD_VERSION_FORMAT_QUAD); + fu_firmware_image_set_version (FU_FIRMWARE_IMAGE (self), version_str); + return TRUE; +} + gboolean fu_ccgx_cyacd_firmware_image_parse_header (FuCcgxCyacdFirmwareImage *self, const gchar *line, diff --git a/plugins/ccgx/fu-ccgx-cyacd-firmware-image.h b/plugins/ccgx/fu-ccgx-cyacd-firmware-image.h index ec4377e01..aab98b6ce 100644 --- a/plugins/ccgx/fu-ccgx-cyacd-firmware-image.h +++ b/plugins/ccgx/fu-ccgx-cyacd-firmware-image.h @@ -22,6 +22,8 @@ FuFirmwareImage *fu_ccgx_cyacd_firmware_image_new (void); gboolean fu_ccgx_cyacd_firmware_image_parse_header (FuCcgxCyacdFirmwareImage *self, const gchar *line, GError **error); +gboolean fu_ccgx_cyacd_firmware_image_parse_md_block (FuCcgxCyacdFirmwareImage *self, + GError **error); gboolean fu_ccgx_cyacd_firmware_image_add_record (FuCcgxCyacdFirmwareImage *self, const gchar *line, GError **error); diff --git a/plugins/ccgx/fu-ccgx-cyacd-firmware.c b/plugins/ccgx/fu-ccgx-cyacd-firmware.c index ce52f1e37..2d2e6978a 100644 --- a/plugins/ccgx/fu-ccgx-cyacd-firmware.c +++ b/plugins/ccgx/fu-ccgx-cyacd-firmware.c @@ -74,6 +74,8 @@ fu_ccgx_cyacd_firmware_parse (FuFirmware *firmware, } for (guint i = 0; i < images->len; i++) { FuFirmwareImage *img = g_ptr_array_index (images, i); + if (!fu_ccgx_cyacd_firmware_image_parse_md_block (FU_CCGX_CYACD_FIRMWARE_IMAGE (img), error)) + return FALSE; fu_firmware_add_image (firmware, img); }