ccgx: Parse the metadata block in the firmware image

This commit is contained in:
Richard Hughes 2020-03-18 18:33:08 +00:00
parent b258e514c5
commit 48d9fb8f74
4 changed files with 129 additions and 0 deletions

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2020 Cypress Semiconductor Corporation.
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <glib-object.h>
/* 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;

View File

@ -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,

View File

@ -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);

View File

@ -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);
}