mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-02 16:45:45 +00:00
195 lines
4.8 KiB
C
195 lines
4.8 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 <fwupdplugin.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;
|
|
if (GUINT16_FROM_LE(self->hdr.vid) == 0x06C4)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
fu_vli_pd_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
|
|
{
|
|
FuVliPdFirmware *self = FU_VLI_PD_FIRMWARE(firmware);
|
|
fu_xmlb_builder_insert_kv(bn,
|
|
"device_kind",
|
|
fu_vli_common_device_kind_to_string(self->device_kind));
|
|
fu_xmlb_builder_insert_kx(bn, "vid", fu_vli_pd_firmware_get_vid(self));
|
|
fu_xmlb_builder_insert_kx(bn, "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->export = fu_vli_pd_firmware_export;
|
|
}
|
|
|
|
FuFirmware *
|
|
fu_vli_pd_firmware_new(void)
|
|
{
|
|
return FU_FIRMWARE(g_object_new(FU_TYPE_VLI_PD_FIRMWARE, NULL));
|
|
}
|