fwupd/plugins/vli/fu-vli-pd-firmware.c
Richard Hughes 1981c63d58 Remove FuFirmwareImage and just use FuFirmware instead
This allows us to 'nest' firmware formats, and removes a ton of duplication.

The aim here is to deprecate FuFirmwareImage -- it's almost always acting
as a 'child' FuFirmware instance, and even copies most of the vfuncs to allow
custom types. If I'm struggling to work out what should be a FuFirmware and
what should be a FuFirmwareImage then a plugin author has no hope.

For simple payloads we were adding bytes into an image and then the image into
a firmware. This gets really messy when most plugins are treating the FuFirmware
*as* the binary firmware file.

The GBytes saved in the FuFirmware would be considered the payload with the
aim of not using FuFirmwareImage in the single-image case.
2021-03-09 21:14:12 +00:00

178 lines
4.7 KiB
C

/*
* Copyright (C) 2017 VIA Corporation
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include "fu-common.h"
#include "fu-vli-pd-common.h"
#include "fu-vli-pd-firmware.h"
struct _FuVliPdFirmware {
FuFirmwareClass parent_instance;
FuVliDeviceKind device_kind;
FuVliPdHdr hdr;
};
G_DEFINE_TYPE (FuVliPdFirmware, fu_vli_pd_firmware, FU_TYPE_FIRMWARE)
FuVliDeviceKind
fu_vli_pd_firmware_get_kind (FuVliPdFirmware *self)
{
g_return_val_if_fail (FU_IS_VLI_PD_FIRMWARE (self), 0);
return self->device_kind;
}
guint16
fu_vli_pd_firmware_get_vid (FuVliPdFirmware *self)
{
g_return_val_if_fail (FU_IS_VLI_PD_FIRMWARE (self), 0);
return GUINT16_FROM_LE (self->hdr.vid);
}
guint16
fu_vli_pd_firmware_get_pid (FuVliPdFirmware *self)
{
g_return_val_if_fail (FU_IS_VLI_PD_FIRMWARE (self), 0);
return GUINT16_FROM_LE (self->hdr.pid);
}
static gboolean
fu_vli_pd_firmware_validate_header (FuVliPdFirmware *self)
{
if (GUINT16_FROM_LE (self->hdr.vid) == 0x2109)
return TRUE;
if (GUINT16_FROM_LE (self->hdr.vid) == 0x17EF)
return TRUE;
if (GUINT16_FROM_LE (self->hdr.vid) == 0x2D01)
return TRUE;
return FALSE;
}
static void
fu_vli_pd_firmware_to_string (FuFirmware *firmware, guint idt, GString *str)
{
FuVliPdFirmware *self = FU_VLI_PD_FIRMWARE (firmware);
fu_common_string_append_kv (str, idt, "DeviceKind",
fu_vli_common_device_kind_to_string (self->device_kind));
fu_common_string_append_kx (str, idt, "VID",
fu_vli_pd_firmware_get_vid (self));
fu_common_string_append_kx (str, idt, "PID",
fu_vli_pd_firmware_get_pid (self));
}
static gboolean
fu_vli_pd_firmware_parse (FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
FuVliPdFirmware *self = FU_VLI_PD_FIRMWARE (firmware);
gsize bufsz = 0;
guint32 fwver;
const guint8 *buf = g_bytes_get_data (fw, &bufsz);
g_autofree gchar *fwver_str = NULL;
/* map header from new offset location */
if (!fu_memcpy_safe ((guint8 *) &self->hdr, sizeof(self->hdr), 0x0,
buf, bufsz, VLI_USBHUB_PD_FLASHMAP_ADDR,
sizeof(self->hdr), error)) {
g_prefix_error (error, "failed to read header: ");
return FALSE;
}
/* fall back to legacy location */
if (!fu_vli_pd_firmware_validate_header (self)) {
if (!fu_memcpy_safe ((guint8 *) &self->hdr, sizeof(self->hdr), 0x0,
buf, bufsz, VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY,
sizeof(self->hdr), error)) {
g_prefix_error (error, "failed to read header: ");
return FALSE;
}
}
/* urgh, not found */
if (!fu_vli_pd_firmware_validate_header (self)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"header invalid, VID not supported");
return FALSE;
}
/* guess device kind from fwver */
fwver = GUINT32_FROM_BE (self->hdr.fwver);
self->device_kind = fu_vli_pd_common_guess_device_kind (fwver);
if (self->device_kind == FU_VLI_DEVICE_KIND_UNKNOWN) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"version invalid, using 0x%x", fwver);
return FALSE;
}
fwver_str = fu_common_version_from_uint32 (fwver, FWUPD_VERSION_FORMAT_QUAD);
fu_firmware_set_version (firmware, fwver_str);
fu_firmware_set_version_raw (firmware, fwver);
/* check size */
if (bufsz != fu_vli_common_device_kind_get_size (self->device_kind)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"size invalid, got 0x%x expected 0x%x",
(guint) bufsz,
fu_vli_common_device_kind_get_size (self->device_kind));
return FALSE;
}
/* check CRC */
if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) {
guint16 crc_actual;
guint16 crc_file = 0x0;
if (!fu_common_read_uint16_safe (buf, bufsz, bufsz - 2, &crc_file,
G_LITTLE_ENDIAN, error)) {
g_prefix_error (error, "failed to read file CRC: ");
return FALSE;
}
crc_actual = fu_common_crc16 (buf, bufsz - 2);
if (crc_actual != crc_file) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"CRC invalid, got 0x%x expected 0x%x",
crc_file, crc_actual);
return FALSE;
}
}
/* whole image */
fu_firmware_set_bytes (firmware, fw);
return TRUE;
}
static void
fu_vli_pd_firmware_init (FuVliPdFirmware *self)
{
fu_firmware_add_flag (FU_FIRMWARE (self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
}
static void
fu_vli_pd_firmware_class_init (FuVliPdFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
klass_firmware->parse = fu_vli_pd_firmware_parse;
klass_firmware->to_string = fu_vli_pd_firmware_to_string;
}
FuFirmware *
fu_vli_pd_firmware_new (void)
{
return FU_FIRMWARE (g_object_new (FU_TYPE_VLI_PD_FIRMWARE, NULL));
}