Use FuFirmware as a container for firmware images

In many plugins we've wanted to use ->prepare_firmware() to parse the firmware
ahead of ->detach() and ->write_firmware() but this has the limitation that it
can only return a single blob of data.

For many devices, multiple binary blobs are required from one parsed image,
for instance providing signatures, config and data blobs that have to be pushed
to the device in different way.

This also means we parse the firmware *before* we ask the user to detach.

Break the internal FuDevice API to support these firmware types as they become
more popular.

This also allows us to move the Intel HEX and SREC parsing out of the dfu plugin
as they are used by a few plugins now, and resolving symbols between plugins
isn't exactly awesome.
This commit is contained in:
Richard Hughes 2019-08-07 11:42:42 +01:00
parent 747f570310
commit 7afd7cba0d
78 changed files with 2795 additions and 1618 deletions

11
data/tests/firmware.shex Normal file
View File

@ -0,0 +1,11 @@
:100000003DEF20F000000000FACF01F0FBCF02F03E
:10001000E9CF03F0EACF04F0E1CF05F0E2CF06F03C
:10002000D9CF07F0DACF08F0F3CF09F0F4CF0AF018
:10003000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF0B8
:100040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FFA8
:100050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFE8
:1000600006C0E2FF05C0E1FF04C0EAFF03C0E9FFEC
:1000700002C0FBFF01C0FAFF11003FEF20F00001BA
:0800800042EF20F03DEF20F0FB
:080000FD6465616462656566DB
:00000001FF

View File

@ -268,19 +268,31 @@ fu_altos_device_write_page (FuAltosDevice *self,
return TRUE; return TRUE;
} }
static FuFirmware *
fu_altos_device_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(FuFirmware) firmware = fu_altos_firmware_new ();
if (!fu_firmware_parse (firmware, fw, flags, error))
return FALSE;
return g_steal_pointer (&firmware);
}
static gboolean static gboolean
fu_altos_device_write_firmware (FuDevice *device, fu_altos_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuAltosDevice *self = FU_ALTOS_DEVICE (device); FuAltosDevice *self = FU_ALTOS_DEVICE (device);
GBytes *fw_blob;
const gchar *data; const gchar *data;
const gsize data_len; const gsize data_len;
guint flash_len; guint flash_len;
g_autoptr(FuAltosFirmware) altos_firmware = NULL;
g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(FuFirmwareImage) img = NULL;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GString) buf = g_string_new (NULL); g_autoptr(GString) buf = g_string_new (NULL);
/* check kind */ /* check kind */
@ -312,25 +324,27 @@ fu_altos_device_write_firmware (FuDevice *device,
} }
/* load ihex blob */ /* load ihex blob */
altos_firmware = fu_altos_firmware_new (); img = fu_firmware_get_image_default (firmware, error);
if (!fu_altos_firmware_parse (altos_firmware, fw, error)) if (img == NULL)
return FALSE; return FALSE;
/* check the start address */ /* check the start address */
if (fu_altos_firmware_get_address (altos_firmware) != self->addr_base) { if (fu_firmware_image_get_addr (img) != self->addr_base) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE, FWUPD_ERROR_INVALID_FILE,
"start address not correct %" G_GUINT64_FORMAT ":" "start address not correct %" G_GUINT64_FORMAT ":"
"%" G_GUINT64_FORMAT, "%" G_GUINT64_FORMAT,
fu_altos_firmware_get_address (altos_firmware), fu_firmware_image_get_addr (img),
self->addr_base); self->addr_base);
return FALSE; return FALSE;
} }
/* check firmware will fit */ /* check firmware will fit */
fw_blob = fu_altos_firmware_get_data (altos_firmware); fw = fu_firmware_image_get_bytes (img, error);
data = g_bytes_get_data (fw_blob, (gsize *) &data_len); if (fw == NULL)
return FALSE;
data = g_bytes_get_data (fw, (gsize *) &data_len);
if (data_len > flash_len) { if (data_len > flash_len) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
@ -628,6 +642,7 @@ fu_altos_device_class_init (FuAltosDeviceClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
klass_device->probe = fu_altos_device_probe; klass_device->probe = fu_altos_device_probe;
klass_device->prepare_firmware = fu_altos_device_prepare_firmware;
klass_device->write_firmware = fu_altos_device_write_firmware; klass_device->write_firmware = fu_altos_device_write_firmware;
klass_device->read_firmware = fu_altos_device_read_firmware; klass_device->read_firmware = fu_altos_device_read_firmware;
object_class->finalize = fu_altos_device_finalize; object_class->finalize = fu_altos_device_finalize;

View File

@ -1,45 +1,34 @@
/* /*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com> * Copyright (C) 2017-2019 Richard Hughes <richard@hughsie.com>
* *
* SPDX-License-Identifier: LGPL-2.1+ * SPDX-License-Identifier: LGPL-2.1+
*/ */
#include "config.h" #include "config.h"
#include <gio/gio.h>
#include <gelf.h> #include <gelf.h>
#include <libelf.h> #include <libelf.h>
#include "fu-altos-firmware.h" #include "fu-altos-firmware.h"
#include "fwupd-error.h"
struct _FuAltosFirmware { struct _FuAltosFirmware {
GObject parent_instance; FuFirmware parent_instance;
GBytes *data;
guint64 address;
}; };
G_DEFINE_TYPE (FuAltosFirmware, fu_altos_firmware, G_TYPE_OBJECT) G_DEFINE_TYPE (FuAltosFirmware, fu_altos_firmware, FU_TYPE_FIRMWARE)
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function" #pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf, elf_end); G_DEFINE_AUTOPTR_CLEANUP_FUNC(Elf, elf_end);
#pragma clang diagnostic pop #pragma clang diagnostic pop
GBytes * static gboolean
fu_altos_firmware_get_data (FuAltosFirmware *self) fu_altos_firmware_parse (FuFirmware *firmware,
{ GBytes *blob,
return self->data; guint64 addr_start,
} guint64 addr_end,
FwupdInstallFlags flags,
guint64 GError **error)
fu_altos_firmware_get_address (FuAltosFirmware *self)
{
return self->address;
}
gboolean
fu_altos_firmware_parse (FuAltosFirmware *self, GBytes *blob, GError **error)
{ {
const gchar *name; const gchar *name;
Elf_Scn *scn = NULL; Elf_Scn *scn = NULL;
@ -99,8 +88,12 @@ fu_altos_firmware_parse (FuAltosFirmware *self, GBytes *blob, GError **error)
if (g_strcmp0 (name, ".text") == 0) { if (g_strcmp0 (name, ".text") == 0) {
Elf_Data *data = elf_getdata (scn, NULL); Elf_Data *data = elf_getdata (scn, NULL);
if (data != NULL && data->d_buf != NULL) { if (data != NULL && data->d_buf != NULL) {
self->data = g_bytes_new (data->d_buf, data->d_size); g_autoptr(FuFirmwareImage) img = NULL;
self->address = shdr.sh_addr; g_autoptr(GBytes) bytes = NULL;
bytes = g_bytes_new (data->d_buf, data->d_size);
img = fu_firmware_image_new (bytes);
fu_firmware_image_set_addr (img, shdr.sh_addr);
fu_firmware_add_image (firmware, img);
} }
return TRUE; return TRUE;
} }
@ -112,22 +105,11 @@ fu_altos_firmware_parse (FuAltosFirmware *self, GBytes *blob, GError **error)
return FALSE; return FALSE;
} }
static void
fu_altos_firmware_finalize (GObject *object)
{
FuAltosFirmware *self = FU_ALTOS_FIRMWARE (object);
if (self->data != NULL)
g_bytes_unref (self->data);
G_OBJECT_CLASS (fu_altos_firmware_parent_class)->finalize (object);
}
static void static void
fu_altos_firmware_class_init (FuAltosFirmwareClass *klass) fu_altos_firmware_class_init (FuAltosFirmwareClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
object_class->finalize = fu_altos_firmware_finalize; klass_firmware->parse = fu_altos_firmware_parse;
} }
static void static void
@ -135,10 +117,8 @@ fu_altos_firmware_init (FuAltosFirmware *self)
{ {
} }
FuAltosFirmware * FuFirmware *
fu_altos_firmware_new (void) fu_altos_firmware_new (void)
{ {
FuAltosFirmware *self; return FU_FIRMWARE (g_object_new (FU_TYPE_ALTOS_FIRMWARE, NULL));
self = g_object_new (FU_TYPE_ALTOS_FIRMWARE, NULL);
return FU_ALTOS_FIRMWARE (self);
} }

View File

@ -1,22 +1,19 @@
/* /*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com> * Copyright (C) 2017-2019 Richard Hughes <richard@hughsie.com>
* *
* SPDX-License-Identifier: LGPL-2.1+ * SPDX-License-Identifier: LGPL-2.1+
*/ */
#pragma once #pragma once
#include "fu-firmware.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define FU_TYPE_ALTOS_FIRMWARE (fu_altos_firmware_get_type ()) #define FU_TYPE_ALTOS_FIRMWARE (fu_altos_firmware_get_type ())
G_DECLARE_FINAL_TYPE (FuAltosFirmware, fu_altos_firmware, FU, ALTOS_FIRMWARE, GObject) G_DECLARE_FINAL_TYPE (FuAltosFirmware, fu_altos_firmware, FU, ALTOS_FIRMWARE, FuFirmware)
FuAltosFirmware *fu_altos_firmware_new (void); FuFirmware *fu_altos_firmware_new (void);
GBytes *fu_altos_firmware_get_data (FuAltosFirmware *self);
guint64 fu_altos_firmware_get_address (FuAltosFirmware *self);
gboolean fu_altos_firmware_parse (FuAltosFirmware *self,
GBytes *blob,
GError **error);
G_END_DECLS G_END_DECLS

View File

@ -578,15 +578,21 @@ fu_ata_device_fw_download (FuAtaDevice *self,
static gboolean static gboolean
fu_ata_device_write_firmware (FuDevice *device, fu_ata_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuAtaDevice *self = FU_ATA_DEVICE (device); FuAtaDevice *self = FU_ATA_DEVICE (device);
guint32 chunksz = (guint32) self->transfer_blocks * FU_ATA_BLOCK_SIZE; guint32 chunksz = (guint32) self->transfer_blocks * FU_ATA_BLOCK_SIZE;
guint max_size = 0xffff * FU_ATA_BLOCK_SIZE; guint max_size = 0xffff * FU_ATA_BLOCK_SIZE;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* only one block allowed */ /* only one block allowed */
if (self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNK) if (self->transfer_mode == ATA_SUBCMD_MICROCODE_DOWNLOAD_CHUNK)
max_size = 0xffff; max_size = 0xffff;

View File

@ -328,13 +328,19 @@ ch_colorhug_device_calculate_checksum (const guint8 *data, guint32 len)
static gboolean static gboolean
fu_colorhug_device_write_firmware (FuDevice *device, fu_colorhug_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuColorhugDevice *self = FU_COLORHUG_DEVICE (device); FuColorhugDevice *self = FU_COLORHUG_DEVICE (device);
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* build packets */ /* build packets */
chunks = fu_chunk_array_new_from_bytes (fw, chunks = fu_chunk_array_new_from_bytes (fw,
self->start_addr, self->start_addr,

View File

@ -10,9 +10,9 @@
#include "fu-chunk.h" #include "fu-chunk.h"
#include "fu-csr-device.h" #include "fu-csr-device.h"
#include "fu-ihex-firmware.h"
#include "dfu-common.h" #include "dfu-common.h"
#include "dfu-firmware.h"
/** /**
* FU_CSR_DEVICE_QUIRK_FLAG_REQUIRE_DELAY: * FU_CSR_DEVICE_QUIRK_FLAG_REQUIRE_DELAY:
@ -416,71 +416,44 @@ fu_csr_device_download_chunk (FuCsrDevice *self, guint16 idx, GBytes *chunk, GEr
return TRUE; return TRUE;
} }
static GBytes * static FuFirmware *
_dfu_firmware_get_default_element_data (DfuFirmware *firmware)
{
DfuElement *element;
DfuImage *image;
image = dfu_firmware_get_image_default (firmware);
if (image == NULL)
return NULL;
element = dfu_image_get_element_default (image);
if (element == NULL)
return NULL;
return dfu_element_get_contents (element);
}
static GBytes *
fu_csr_device_prepare_firmware (FuDevice *device, fu_csr_device_prepare_firmware (FuDevice *device,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
GBytes *blob_noftr; g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new ();
g_autoptr(DfuFirmware) dfu_firmware = dfu_firmware_new ();
/* parse the file */ /* parse the file */
if (!dfu_firmware_parse_data (dfu_firmware, fw, if (!fu_firmware_parse (firmware, fw, flags, error))
DFU_FIRMWARE_PARSE_FLAG_NONE, error))
return NULL; return NULL;
if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) { if (g_getenv ("FWUPD_CSR_VERBOSE") != NULL) {
g_autofree gchar *fw_str = NULL; g_autofree gchar *fw_str = NULL;
fw_str = dfu_firmware_to_string (dfu_firmware); fw_str = fu_firmware_to_string (firmware);
g_debug ("%s", fw_str); g_debug ("%s", fw_str);
} }
if (dfu_firmware_get_format (dfu_firmware) != DFU_FIRMWARE_FORMAT_DFU) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"expected DFU firmware");
return NULL;
}
/* get the blob from the firmware file */
blob_noftr = _dfu_firmware_get_default_element_data (dfu_firmware);
if (blob_noftr == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"firmware contained no data");
return NULL;
}
/* success */ /* success */
return g_bytes_ref (blob_noftr); return g_steal_pointer (&firmware);
} }
static gboolean static gboolean
fu_csr_device_download (FuDevice *device, fu_csr_device_download (FuDevice *device,
GBytes *blob, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuCsrDevice *self = FU_CSR_DEVICE (device); FuCsrDevice *self = FU_CSR_DEVICE (device);
guint16 idx; guint16 idx;
g_autoptr(GBytes) blob_empty = NULL; g_autoptr(GBytes) blob_empty = NULL;
g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* get default image */
blob = fu_firmware_get_image_default_bytes (firmware, error);
if (blob == NULL)
return FALSE;
/* notify UI */ /* notify UI */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);

View File

@ -46,22 +46,29 @@ fu_dell_dock_hub_probe (FuDevice *device, GError **error)
static gboolean static gboolean
fu_dell_dock_hub_write_fw (FuDevice *device, fu_dell_dock_hub_write_fw (FuDevice *device,
GBytes *blob_fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuDellDockHub *self = FU_DELL_DOCK_HUB (device); FuDellDockHub *self = FU_DELL_DOCK_HUB (device);
gsize fw_size = 0; gsize fw_size = 0;
const guint8 *data = g_bytes_get_data (blob_fw, &fw_size); const guint8 *data;
gsize write_size = gsize write_size =
(fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size;
gsize nwritten = 0; gsize nwritten = 0;
guint32 address = 0; guint32 address = 0;
gboolean result = FALSE; gboolean result = FALSE;
g_autofree gchar *dynamic_version = NULL; g_autofree gchar *dynamic_version = NULL;
g_autoptr(GBytes) fw = NULL;
g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (device != NULL, FALSE);
g_return_val_if_fail (blob_fw != NULL, FALSE); g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE);
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
data = g_bytes_get_data (fw, &fw_size);
dynamic_version = g_strdup_printf ("%02x.%02x", dynamic_version = g_strdup_printf ("%02x.%02x",
data[self->blob_major_offset], data[self->blob_major_offset],

View File

@ -745,7 +745,7 @@ fu_dell_dock_ec_commit_package (FuDevice *device, GBytes *blob_fw,
static gboolean static gboolean
fu_dell_dock_ec_write_fw (FuDevice *device, fu_dell_dock_ec_write_fw (FuDevice *device,
GBytes *blob_fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
@ -753,15 +753,22 @@ fu_dell_dock_ec_write_fw (FuDevice *device,
FuDellDockECFWUpdateStatus status = FW_UPDATE_IN_PROGRESS; FuDellDockECFWUpdateStatus status = FW_UPDATE_IN_PROGRESS;
guint8 progress1 = 0, progress0 = 0; guint8 progress1 = 0, progress0 = 0;
gsize fw_size = 0; gsize fw_size = 0;
const guint8 *data = g_bytes_get_data (blob_fw, &fw_size); const guint8 *data;
gsize write_size = gsize write_size =
(fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size; (fw_size / HIDI2C_MAX_WRITE) >= 1 ? HIDI2C_MAX_WRITE : fw_size;
gsize nwritten = 0; gsize nwritten = 0;
guint32 address = 0 | 0xff << 24; guint32 address = 0 | 0xff << 24;
g_autofree gchar *dynamic_version = NULL; g_autofree gchar *dynamic_version = NULL;
g_autoptr(GBytes) fw = NULL;
g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (device != NULL, FALSE);
g_return_val_if_fail (blob_fw != NULL, FALSE); g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE);
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
data = g_bytes_get_data (fw, &fw_size);
dynamic_version = g_strndup ((gchar *) data + self->blob_version_offset, 11); dynamic_version = g_strndup ((gchar *) data + self->blob_version_offset, 11);
g_debug ("writing EC firmware version %s", dynamic_version); g_debug ("writing EC firmware version %s", dynamic_version);

View File

@ -735,7 +735,7 @@ fu_dell_dock_mst_invalidate_bank (FuDevice *symbiote, MSTBank bank_in_use,
static gboolean static gboolean
fu_dell_dock_mst_write_fw (FuDevice *device, fu_dell_dock_mst_write_fw (FuDevice *device,
GBytes *blob_fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
@ -745,13 +745,20 @@ fu_dell_dock_mst_write_fw (FuDevice *device,
gboolean checksum = FALSE; gboolean checksum = FALSE;
guint8 order[2] = {ESM, Bank0}; guint8 order[2] = {ESM, Bank0};
guint16 chip_id; guint16 chip_id;
const guint8* data = g_bytes_get_data (blob_fw, NULL); const guint8 *data;
g_autofree gchar *dynamic_version = NULL; g_autofree gchar *dynamic_version = NULL;
g_autoptr(GBytes) fw = NULL;
g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (device != NULL, FALSE);
g_return_val_if_fail (blob_fw != NULL, FALSE); g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE);
g_return_val_if_fail (self->symbiote != NULL, FALSE); g_return_val_if_fail (self->symbiote != NULL, FALSE);
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
data = g_bytes_get_data (fw, NULL);
dynamic_version = g_strdup_printf ("%02x.%02x.%02x", dynamic_version = g_strdup_printf ("%02x.%02x.%02x",
data[self->blob_major_offset], data[self->blob_major_offset],
data[self->blob_minor_offset], data[self->blob_minor_offset],
@ -787,7 +794,7 @@ fu_dell_dock_mst_write_fw (FuDevice *device,
for (guint phase = 0; phase < 2; phase++) { for (guint phase = 0; phase < 2; phase++) {
g_debug ("MST: Checking bank %u", order[phase]); g_debug ("MST: Checking bank %u", order[phase]);
if (!fu_dell_dock_mst_checksum_bank (self->symbiote, if (!fu_dell_dock_mst_checksum_bank (self->symbiote,
blob_fw, fw,
order[phase], order[phase],
&checksum, error)) &checksum, error))
return FALSE; return FALSE;
@ -804,11 +811,11 @@ fu_dell_dock_mst_write_fw (FuDevice *device,
error)) error))
return FALSE; return FALSE;
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);
if (!fu_dell_dock_write_flash_bank (device, blob_fw, if (!fu_dell_dock_write_flash_bank (device, fw,
order[phase], error)) order[phase], error))
return FALSE; return FALSE;
if (!fu_dell_dock_mst_checksum_bank (self->symbiote, if (!fu_dell_dock_mst_checksum_bank (self->symbiote,
blob_fw, fw,
order[phase], order[phase],
&checksum, &checksum,
error)) error))

View File

@ -53,20 +53,27 @@ G_DEFINE_TYPE (FuDellDockTbt, fu_dell_dock_tbt, FU_TYPE_DEVICE)
static gboolean static gboolean
fu_dell_dock_tbt_write_fw (FuDevice *device, fu_dell_dock_tbt_write_fw (FuDevice *device,
GBytes *blob_fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuDellDockTbt *self = FU_DELL_DOCK_TBT (device); FuDellDockTbt *self = FU_DELL_DOCK_TBT (device);
guint32 start_offset = 0; guint32 start_offset = 0;
gsize image_size; gsize image_size = 0;
const guint8 *buffer = g_bytes_get_data (blob_fw, &image_size); const guint8 *buffer;
guint16 target_system = 0; guint16 target_system = 0;
g_autoptr(GTimer) timer = g_timer_new (); g_autoptr(GTimer) timer = g_timer_new ();
g_autofree gchar *dynamic_version = NULL; g_autofree gchar *dynamic_version = NULL;
g_autoptr(GBytes) fw = NULL;
g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (device != NULL, FALSE);
g_return_val_if_fail (blob_fw != NULL, FALSE); g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE);
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
buffer = g_bytes_get_data (fw, &image_size);
dynamic_version = g_strdup_printf ("%02x.%02x", dynamic_version = g_strdup_printf ("%02x.%02x",
buffer[self->blob_major_offset], buffer[self->blob_major_offset],

View File

@ -58,7 +58,7 @@ fu_dell_dock_status_setup (FuDevice *device, GError **error)
static gboolean static gboolean
fu_dell_dock_status_write (FuDevice *device, fu_dell_dock_status_write (FuDevice *device,
GBytes *blob_fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
@ -66,18 +66,25 @@ fu_dell_dock_status_write (FuDevice *device,
FuDevice *parent; FuDevice *parent;
gsize length = 0; gsize length = 0;
guint32 status_version = 0; guint32 status_version = 0;
const guint8 *data = g_bytes_get_data (blob_fw, &length); const guint8 *data;
g_autofree gchar *dynamic_version = NULL; g_autofree gchar *dynamic_version = NULL;
g_autoptr(GBytes) fw = NULL;
g_return_val_if_fail (device != NULL, FALSE); g_return_val_if_fail (device != NULL, FALSE);
g_return_val_if_fail (blob_fw != NULL, FALSE); g_return_val_if_fail (FU_IS_FIRMWARE (firmware), FALSE);
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
data = g_bytes_get_data (fw, &length);
memcpy (&status_version, data + self->blob_version_offset, sizeof (guint32)); memcpy (&status_version, data + self->blob_version_offset, sizeof (guint32));
dynamic_version = fu_dell_dock_status_ver_string (status_version); dynamic_version = fu_dell_dock_status_ver_string (status_version);
g_debug ("writing status firmware version %s", dynamic_version); g_debug ("writing status firmware version %s", dynamic_version);
parent = fu_device_get_parent (device); parent = fu_device_get_parent (device);
if (!fu_dell_dock_ec_commit_package (parent, blob_fw, error)) if (!fu_dell_dock_ec_commit_package (parent, fw, error))
return FALSE; return FALSE;
/* dock will reboot to re-read; this is to appease the daemon */ /* dock will reboot to re-read; this is to appease the daemon */

View File

@ -176,157 +176,3 @@ dfu_utils_bytes_join_array (GPtrArray *chunks)
} }
return g_bytes_new_take (buffer, total_size); return g_bytes_new_take (buffer, total_size);
} }
/**
* dfu_utils_bytes_pad:
* @bytes: a #GBytes
* @sz: the desired size in bytes
*
* Pads a GBytes to a given @sz with `0xff`.
*
* Return value: (transfer full): a #GBytes
**/
GBytes *
dfu_utils_bytes_pad (GBytes *bytes, gsize sz)
{
gsize bytes_sz;
g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
/* pad */
bytes_sz = g_bytes_get_size (bytes);
if (bytes_sz < sz) {
const guint8 *data = g_bytes_get_data (bytes, NULL);
guint8 *data_new = g_malloc (sz);
memcpy (data_new, data, bytes_sz);
memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
return g_bytes_new_take (data_new, sz);
}
/* exactly right */
return g_bytes_ref (bytes);
}
/**
* dfu_utils_buffer_parse_uint4:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 1 byte long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint8
dfu_utils_buffer_parse_uint4 (const gchar *data)
{
gchar buffer[2];
memcpy (buffer, data, 1);
buffer[1] = '\0';
return (guint8) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* dfu_utils_buffer_parse_uint8:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 2 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint8
dfu_utils_buffer_parse_uint8 (const gchar *data)
{
gchar buffer[3];
memcpy (buffer, data, 2);
buffer[2] = '\0';
return (guint8) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* dfu_utils_buffer_parse_uint16:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 4 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint16
dfu_utils_buffer_parse_uint16 (const gchar *data)
{
gchar buffer[5];
memcpy (buffer, data, 4);
buffer[4] = '\0';
return (guint16) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* dfu_utils_buffer_parse_uint24:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 6 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint32
dfu_utils_buffer_parse_uint24 (const gchar *data)
{
gchar buffer[7];
memcpy (buffer, data, 6);
buffer[6] = '\0';
return (guint32) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* dfu_utils_buffer_parse_uint32:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 8 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint32
dfu_utils_buffer_parse_uint32 (const gchar *data)
{
gchar buffer[9];
memcpy (buffer, data, 8);
buffer[8] = '\0';
return (guint32) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* dfu_utils_strnsplit:
* @str: a string to split
* @sz: size of @str
* @delimiter: a string which specifies the places at which to split the string
* @max_tokens: the maximum number of pieces to split @str into
*
* Splits a string into a maximum of @max_tokens pieces, using the given
* delimiter. If @max_tokens is reached, the remainder of string is appended
* to the last token.
*
* Return value: a newly-allocated NULL-terminated array of strings
**/
gchar **
dfu_utils_strnsplit (const gchar *str, gsize sz,
const gchar *delimiter, gint max_tokens)
{
if (str[sz - 1] != '\0') {
g_autofree gchar *str2 = g_strndup (str, sz);
return g_strsplit (str2, delimiter, max_tokens);
}
return g_strsplit (str, delimiter, max_tokens);
}

View File

@ -156,16 +156,5 @@ const gchar *dfu_version_to_string (DfuVersion version);
/* helpers */ /* helpers */
GBytes *dfu_utils_bytes_join_array (GPtrArray *chunks); GBytes *dfu_utils_bytes_join_array (GPtrArray *chunks);
GBytes *dfu_utils_bytes_pad (GBytes *bytes,
gsize sz);
guint8 dfu_utils_buffer_parse_uint4 (const gchar *data);
guint8 dfu_utils_buffer_parse_uint8 (const gchar *data);
guint16 dfu_utils_buffer_parse_uint16 (const gchar *data);
guint32 dfu_utils_buffer_parse_uint24 (const gchar *data);
guint32 dfu_utils_buffer_parse_uint32 (const gchar *data);
gchar **dfu_utils_strnsplit (const gchar *str,
gsize sz,
const gchar *delimiter,
gint max_tokens);
G_END_DECLS G_END_DECLS

View File

@ -86,6 +86,7 @@
#include "dfu-target-stm.h" #include "dfu-target-stm.h"
#include "fu-device-locker.h" #include "fu-device-locker.h"
#include "fu-firmware-common.h"
#include "fwupd-error.h" #include "fwupd-error.h"
@ -1120,8 +1121,8 @@ dfu_device_detach (DfuDevice *device, GError **error)
g_autoptr(GError) error_jabra = NULL; g_autoptr(GError) error_jabra = NULL;
/* parse string and create magic packet */ /* parse string and create magic packet */
rep = dfu_utils_buffer_parse_uint8 (priv->jabra_detach + 0); rep = fu_firmware_strparse_uint8 (priv->jabra_detach + 0);
adr = dfu_utils_buffer_parse_uint8 (priv->jabra_detach + 2); adr = fu_firmware_strparse_uint8 (priv->jabra_detach + 2);
buf[0] = rep; buf[0] = rep;
buf[1] = adr; buf[1] = adr;
buf[2] = 0x00; buf[2] = 0x00;
@ -2052,7 +2053,7 @@ dfu_device_set_quirk_kv (FuDevice *device,
} }
if (g_strcmp0 (key, FU_QUIRKS_DFU_FORCE_VERSION) == 0) { if (g_strcmp0 (key, FU_QUIRKS_DFU_FORCE_VERSION) == 0) {
if (value != NULL && strlen (value) == 4) { if (value != NULL && strlen (value) == 4) {
priv->force_version = dfu_utils_buffer_parse_uint16 (value); priv->force_version = fu_firmware_strparse_uint16 (value);
return TRUE; return TRUE;
} }
g_set_error_literal (error, g_set_error_literal (error,

View File

@ -239,60 +239,3 @@ dfu_element_set_target_size (DfuElement *element, guint32 target_size)
g_bytes_unref (priv->contents); g_bytes_unref (priv->contents);
priv->contents = g_bytes_new_take (buf, target_size); priv->contents = g_bytes_new_take (buf, target_size);
} }
/**
* dfu_element_get_contents_chunk:
* @element: a #DfuElement
* @address: an address greater than dfu_element_get_address()
* @chunk_sz_max: the size of the new chunk
* @error: a #GError, or %NULL
*
* Gets a block of data from the @element. If the contents of the element is
* smaller than the requested chunk size then the #GBytes will be smaller
* than @chunk_sz_max. Use dfu_utils_bytes_pad() if padding is required.
*
* If the @address is larger than the size of the @element then an error is returned.
*
* Return value: (transfer full): a #GBytes, or %NULL
**/
GBytes *
dfu_element_get_contents_chunk (DfuElement *element,
guint32 address,
guint32 chunk_sz_max,
GError **error)
{
GBytes *blob;
gsize chunk_left;
guint32 offset;
/* check address requested is larger than base address */
if (address < dfu_element_get_address (element)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"requested address 0x%x less than base address 0x%x",
(guint) address, (guint) dfu_element_get_address (element));
return NULL;
}
/* offset into data */
offset = address - dfu_element_get_address (element);
blob = dfu_element_get_contents (element);
if (offset > g_bytes_get_size (blob)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"offset 0x%x larger than data size 0x%x",
(guint) offset,
(guint) g_bytes_get_size (blob));
return NULL;
}
/* if we have less data than requested */
chunk_left = g_bytes_get_size (blob) - offset;
if (chunk_sz_max > chunk_left)
return g_bytes_new_from_bytes (blob, offset, chunk_left);
/* check chunk */
return g_bytes_new_from_bytes (blob, offset, chunk_sz_max);
}

View File

@ -23,11 +23,6 @@ DfuElement *dfu_element_new (void);
GBytes *dfu_element_get_contents (DfuElement *element); GBytes *dfu_element_get_contents (DfuElement *element);
guint32 dfu_element_get_address (DfuElement *element); guint32 dfu_element_get_address (DfuElement *element);
GBytes *dfu_element_get_contents_chunk (DfuElement *element,
guint32 address,
guint32 chunk_sz_max,
GError **error);
void dfu_element_set_contents (DfuElement *element, void dfu_element_set_contents (DfuElement *element,
GBytes *contents); GBytes *contents);
void dfu_element_set_address (DfuElement *element, void dfu_element_set_address (DfuElement *element,

View File

@ -15,6 +15,9 @@
#include "dfu-format-ihex.h" #include "dfu-format-ihex.h"
#include "dfu-image.h" #include "dfu-image.h"
#include "fu-firmware-common.h"
#include "fu-ihex-firmware.h"
#include "fwupd-error.h" #include "fwupd-error.h"
/** /**
@ -46,34 +49,6 @@ dfu_firmware_detect_ihex (GBytes *bytes)
return DFU_FIRMWARE_FORMAT_UNKNOWN; return DFU_FIRMWARE_FORMAT_UNKNOWN;
} }
#define DFU_INHX32_RECORD_TYPE_DATA 0x00
#define DFU_INHX32_RECORD_TYPE_EOF 0x01
#define DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT 0x02
#define DFU_INHX32_RECORD_TYPE_START_SEGMENT 0x03
#define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04
#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05
#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd
static const gchar *
dfu_firmware_ihex_record_type_to_string (guint8 record_type)
{
if (record_type == DFU_INHX32_RECORD_TYPE_DATA)
return "DATA";
if (record_type == DFU_INHX32_RECORD_TYPE_EOF)
return "EOF";
if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT)
return "EXTENDED_SEGMENT";
if (record_type == DFU_INHX32_RECORD_TYPE_START_SEGMENT)
return "START_SEGMENT";
if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR)
return "EXTENDED_LINEAR";
if (record_type == DFU_INHX32_RECORD_TYPE_START_LINEAR)
return "ADDR32";
if (record_type == DFU_INHX32_RECORD_TYPE_SIGNATURE)
return "SIGNATURE";
return NULL;
}
/** /**
* dfu_firmware_from_ihex: (skip) * dfu_firmware_from_ihex: (skip)
* @firmware: a #DfuFirmware * @firmware: a #DfuFirmware
@ -91,315 +66,28 @@ dfu_firmware_from_ihex (DfuFirmware *firmware,
DfuFirmwareParseFlags flags, DfuFirmwareParseFlags flags,
GError **error) GError **error)
{ {
const gchar *data; g_autoptr(FuFirmware) firmware_new = fu_ihex_firmware_new ();
gboolean got_eof = FALSE; g_autoptr(GPtrArray) imgs = NULL;
gsize sz = 0; FwupdInstallFlags flags_new = FWUPD_INSTALL_FLAG_NONE;
guint32 abs_addr = 0x0;
guint32 addr_last = 0x0;
guint32 base_addr = G_MAXUINT32;
guint32 seg_addr = 0x0;
g_auto(GStrv) lines = NULL;
g_autoptr(DfuElement) element = NULL;
g_autoptr(DfuImage) image = NULL;
g_autoptr(GBytes) contents = NULL;
g_autoptr(GString) buf = g_string_new (NULL);
g_autoptr(GString) buf_signature = g_string_new (NULL);
g_return_val_if_fail (bytes != NULL, FALSE); /* make a native objects from the abstract firmware */
if (flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST)
/* create element */ flags_new |= FWUPD_INSTALL_FLAG_FORCE;
image = dfu_image_new (); if (!fu_firmware_parse (firmware_new, bytes, flags_new, error))
dfu_image_set_name (image, "ihex");
element = dfu_element_new ();
/* parse records */
data = g_bytes_get_data (bytes, &sz);
lines = dfu_utils_strnsplit (data, sz, "\n", -1);
for (guint ln = 0; lines[ln] != NULL; ln++) {
const gchar *line = lines[ln];
gsize linesz;
guint32 addr;
guint8 byte_cnt;
guint8 record_type;
guint line_end;
/* ignore comments */
if (g_str_has_prefix (line, ";"))
continue;
/* ignore blank lines */
g_strdelimit (lines[ln], "\r\x1a", '\0');
linesz = strlen (line);
if (linesz == 0)
continue;
/* check starting token */
if (line[0] != ':') {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid starting token on line %u: %s",
ln + 1, line);
return FALSE;
}
/* check there's enough data for the smallest possible record */
if (linesz < 11) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"line %u is incomplete, length %u",
ln + 1, (guint) linesz);
return FALSE;
}
/* length, 16-bit address, type */
byte_cnt = dfu_utils_buffer_parse_uint8 (line + 1);
addr = dfu_utils_buffer_parse_uint16 (line + 3);
record_type = dfu_utils_buffer_parse_uint8 (line + 7);
g_debug ("%s:", dfu_firmware_ihex_record_type_to_string (record_type));
g_debug (" addr_start:\t0x%04x", addr);
g_debug (" length:\t0x%02x", byte_cnt);
addr += seg_addr;
addr += abs_addr;
g_debug (" addr:\t0x%08x", addr);
/* position of checksum */
line_end = 9 + byte_cnt * 2;
if (line_end > (guint) linesz) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"line %u malformed, length: %u",
ln + 1, line_end);
return FALSE;
}
/* verify checksum */
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) {
guint8 checksum = 0;
for (guint i = 1; i < line_end + 2; i += 2) {
guint8 data_tmp = dfu_utils_buffer_parse_uint8 (line + i);
checksum += data_tmp;
}
if (checksum != 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"line %u has invalid checksum (0x%02x)",
ln + 1, checksum);
return FALSE;
}
}
/* process different record types */
switch (record_type) {
case DFU_INHX32_RECORD_TYPE_DATA:
/* base address for element */
if (base_addr == G_MAXUINT32)
base_addr = addr;
/* does not make sense */
if (addr < addr_last) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid address 0x%x, last was 0x%x",
(guint) addr,
(guint) addr_last);
return FALSE;
}
/* parse bytes from line */
g_debug ("writing data 0x%08x", (guint32) addr);
for (guint i = 9; i < line_end; i += 2) {
/* any holes in the hex record */
guint32 len_hole = addr - addr_last;
guint8 data_tmp;
if (addr_last > 0 && len_hole > 0x100000) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"hole of 0x%x bytes too large to fill",
(guint) len_hole);
return FALSE;
}
if (addr_last > 0x0 && len_hole > 1) {
g_debug ("filling address 0x%08x to 0x%08x",
addr_last + 1, addr_last + len_hole - 1);
for (guint j = 1; j < len_hole; j++) {
/* although 0xff might be clearer,
* we can't write 0xffff to pic14 */
g_string_append_c (buf, 0x00);
}
}
/* write into buf */
data_tmp = dfu_utils_buffer_parse_uint8 (line + i);
g_string_append_c (buf, (gchar) data_tmp);
addr_last = addr++;
}
break;
case DFU_INHX32_RECORD_TYPE_EOF:
if (got_eof) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"duplicate EOF, perhaps "
"corrupt file");
return FALSE;
}
got_eof = TRUE;
break;
case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR:
abs_addr = dfu_utils_buffer_parse_uint16 (line + 9) << 16;
g_debug (" abs_addr:\t0x%02x", abs_addr);
break;
case DFU_INHX32_RECORD_TYPE_START_LINEAR:
abs_addr = dfu_utils_buffer_parse_uint32 (line + 9);
g_debug (" abs_addr:\t0x%08x", abs_addr);
break;
case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT:
/* segment base address, so ~1Mb addressable */
seg_addr = dfu_utils_buffer_parse_uint16 (line + 9) * 16;
g_debug (" seg_addr:\t0x%08x", seg_addr);
break;
case DFU_INHX32_RECORD_TYPE_START_SEGMENT:
/* initial content of the CS:IP registers */
seg_addr = dfu_utils_buffer_parse_uint32 (line + 9);
g_debug (" seg_addr:\t0x%02x", seg_addr);
break;
case DFU_INHX32_RECORD_TYPE_SIGNATURE:
for (guint i = 9; i < line_end; i += 2) {
guint8 tmp_c = dfu_utils_buffer_parse_uint8 (line + i);
g_string_append_c (buf_signature, tmp_c);
}
break;
default:
/* vendors sneak in nonstandard sections past the EOF */
if (got_eof)
break;
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid ihex record type %i",
record_type);
return FALSE;
}
}
/* no EOF */
if (!got_eof) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no EOF, perhaps truncated file");
return FALSE; return FALSE;
} imgs = fu_firmware_get_images (firmware_new);
for (guint i = 0; i < imgs->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (imgs, i);
g_autoptr(DfuElement) element = dfu_element_new ();
g_autoptr(DfuImage) image = dfu_image_new ();
dfu_element_set_contents (element, fu_firmware_image_get_bytes (img, NULL));
dfu_element_set_address (element, fu_firmware_image_get_addr (img));
dfu_image_add_element (image, element);
dfu_image_set_name (image, "ihex");
dfu_firmware_add_image (firmware, image);
/* add single image */ }
contents = g_bytes_new (buf->str, buf->len);
dfu_element_set_contents (element, contents);
if (base_addr != G_MAXUINT32)
dfu_element_set_address (element, base_addr);
dfu_image_add_element (image, element);
dfu_firmware_add_image (firmware, image);
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX); dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX);
/* add optional signature */
if (buf_signature->len > 0) {
g_autoptr(DfuElement) element_sig = dfu_element_new ();
g_autoptr(DfuImage) image_sig = dfu_image_new ();
g_autoptr(GBytes) data_sig = g_bytes_new_static (buf_signature->str, buf_signature->len);
dfu_element_set_contents (element_sig, data_sig);
dfu_image_add_element (image_sig, element_sig);
dfu_image_set_name (image_sig, "signature");
dfu_firmware_add_image (firmware, image_sig);
}
return TRUE;
}
static void
dfu_firmware_ihex_emit_chunk (GString *str,
guint16 address,
guint8 record_type,
const guint8 *data,
gsize sz)
{
guint8 checksum = 0x00;
g_string_append_printf (str, ":%02X%04X%02X",
(guint) sz,
(guint) address,
(guint) record_type);
for (gsize j = 0; j < sz; j++)
g_string_append_printf (str, "%02X", data[j]);
checksum = (guint8) sz;
checksum += (guint8) ((address & 0xff00) >> 8);
checksum += (guint8) (address & 0xff);
checksum += record_type;
for (gsize j = 0; j < sz; j++)
checksum += data[j];
g_string_append_printf (str, "%02X\n", (guint) (((~checksum) + 0x01) & 0xff));
}
static void
dfu_firmware_to_ihex_bytes (GString *str, guint8 record_type,
guint32 address, GBytes *contents)
{
const guint8 *data;
const guint chunk_size = 16;
gsize len;
guint32 address_offset_last = 0x0;
/* get number of chunks */
data = g_bytes_get_data (contents, &len);
for (gsize i = 0; i < len; i += chunk_size) {
guint32 address_tmp = address + i;
guint32 address_offset = (address_tmp >> 16) & 0xffff;
gsize chunk_len = MIN (len - i, 16);
/* need to offset */
if (address_offset != address_offset_last) {
guint8 buf[2];
fu_common_write_uint16 (buf, address_offset, G_BIG_ENDIAN);
dfu_firmware_ihex_emit_chunk (str, 0x0,
DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR,
buf, 2);
address_offset_last = address_offset;
}
address_tmp &= 0xffff;
dfu_firmware_ihex_emit_chunk (str, address_tmp,
record_type, data + i, chunk_len);
}
}
static gboolean
dfu_firmware_to_ihex_element (DfuElement *element, GString *str,
guint8 record_type, GError **error)
{
GBytes *contents = dfu_element_get_contents (element);
dfu_firmware_to_ihex_bytes (str, record_type,
dfu_element_get_address (element),
contents);
return TRUE;
}
static gboolean
dfu_firmware_to_ihex_image (DfuImage *image, GString *str, GError **error)
{
GPtrArray *elements;
guint8 record_type = DFU_INHX32_RECORD_TYPE_DATA;
if (g_strcmp0 (dfu_image_get_name (image), "signature") == 0)
record_type = DFU_INHX32_RECORD_TYPE_SIGNATURE;
elements = dfu_image_get_elements (image);
for (guint i = 0; i < elements->len; i++) {
DfuElement *element = g_ptr_array_index (elements, i);
if (!dfu_firmware_to_ihex_element (element,
str,
record_type,
error))
return FALSE;
}
return TRUE; return TRUE;
} }
@ -416,18 +104,21 @@ GBytes *
dfu_firmware_to_ihex (DfuFirmware *firmware, GError **error) dfu_firmware_to_ihex (DfuFirmware *firmware, GError **error)
{ {
GPtrArray *images; GPtrArray *images;
g_autoptr(GString) str = NULL; g_autoptr(FuFirmware) firmware_new = fu_ihex_firmware_new ();
/* write all the element data */ /* make a new object from the native firmware */
str = g_string_new ("");
images = dfu_firmware_get_images (firmware); images = dfu_firmware_get_images (firmware);
for (guint i = 0; i < images->len; i++) { for (guint i = 0; i < images->len; i++) {
DfuImage *image = g_ptr_array_index (images, i); DfuImage *image = g_ptr_array_index (images, i);
if (!dfu_firmware_to_ihex_image (image, str, error)) GPtrArray *elements = dfu_image_get_elements (image);
return NULL; for (guint j = 0; j < elements->len; j++) {
DfuElement *element = g_ptr_array_index (elements, j);
g_autoptr(FuFirmwareImage) img = NULL;
img = fu_firmware_image_new (dfu_element_get_contents (element));
fu_firmware_image_set_id (img, dfu_image_get_name (image));
fu_firmware_image_set_addr (img, dfu_element_get_address (element));
fu_firmware_add_image (firmware_new, img);
}
} }
return fu_firmware_write (firmware_new, error);
/* add EOF */
dfu_firmware_ihex_emit_chunk (str, 0x0, DFU_INHX32_RECORD_TYPE_EOF, NULL, 0);
return g_bytes_new (str->str, str->len);
} }

View File

@ -10,11 +10,13 @@
#include "fu-common.h" #include "fu-common.h"
#include "dfu-element.h"
#include "dfu-firmware.h" #include "dfu-firmware.h"
#include "dfu-format-srec.h" #include "dfu-format-srec.h"
#include "dfu-image.h" #include "dfu-image.h"
#include "fu-firmware-common.h"
#include "fu-srec-firmware.h"
#include "fwupd-error.h" #include "fwupd-error.h"
/** /**
@ -38,282 +40,6 @@ dfu_firmware_detect_srec (GBytes *bytes)
return DFU_FIRMWARE_FORMAT_SREC; return DFU_FIRMWARE_FORMAT_SREC;
} }
/**
* dfu_firmware_from_srec: (skip)
* @firmware: a #DfuFirmware
* @bytes: data to parse
* @flags: some #DfuFirmwareParseFlags
* @error: a #GError, or %NULL
*
* Unpacks into a firmware object from raw data.
*
* Returns: %TRUE for success
**/
gboolean
dfu_image_from_srec (DfuImage *image,
GBytes *bytes,
guint32 start_addr,
DfuFirmwareParseFlags flags,
GError **error)
{
const gchar *data;
gboolean got_eof = FALSE;
gboolean got_hdr = FALSE;
gsize sz = 0;
guint16 data_cnt = 0;
guint32 addr32_last = 0;
guint32 element_address = 0;
g_auto(GStrv) lines = NULL;
g_autoptr(DfuElement) element = dfu_element_new ();
g_autoptr(GBytes) contents = NULL;
g_autoptr(GString) outbuf = g_string_new (NULL);
g_return_val_if_fail (bytes != NULL, FALSE);
/* parse records */
data = g_bytes_get_data (bytes, &sz);
lines = dfu_utils_strnsplit (data, sz, "\n", -1);
for (guint ln = 0; lines[ln] != NULL; ln++) {
const gchar *line = lines[ln];
gsize linesz;
guint32 rec_addr32;
guint8 addrsz = 0; /* bytes */
guint8 rec_count; /* words */
guint8 rec_kind;
/* ignore blank lines */
g_strdelimit (lines[ln], "\r", '\0');
linesz = strlen (line);
if (linesz == 0)
continue;
/* check starting token */
if (line[0] != 'S') {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid starting token, got '%c' at line %u",
line[0], ln);
return FALSE;
}
/* check there's enough data for the smallest possible record */
if (linesz < 10) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"record incomplete at line %u, length %u",
ln, (guint) linesz);
return FALSE;
}
/* kind, count, address, (data), checksum, linefeed */
rec_kind = line[1] - '0';
rec_count = dfu_utils_buffer_parse_uint8 (line + 2);
if (rec_count * 2 != linesz - 4) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"count incomplete at line %u, "
"length %u, expected %u",
ln, (guint) linesz - 4, (guint) rec_count * 2);
return FALSE;
}
/* checksum check */
if ((flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST) == 0) {
guint8 rec_csum = 0;
guint8 rec_csum_expected;
for (guint8 i = 0; i < rec_count; i++)
rec_csum += dfu_utils_buffer_parse_uint8 (line + (i * 2) + 2);
rec_csum ^= 0xff;
rec_csum_expected = dfu_utils_buffer_parse_uint8 (line + (rec_count * 2) + 2);
if (rec_csum != rec_csum_expected) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"checksum incorrect line %u, "
"expected %02x, got %02x",
ln, rec_csum_expected, rec_csum);
return FALSE;
}
}
/* set each command settings */
switch (rec_kind) {
case 0:
addrsz = 2;
if (got_hdr) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"duplicate header record");
return FALSE;
}
got_hdr = TRUE;
break;
case 1:
addrsz = 2;
break;
case 2:
addrsz = 3;
break;
case 3:
addrsz = 4;
break;
case 5:
addrsz = 2;
got_eof = TRUE;
break;
case 6:
addrsz = 3;
break;
case 7:
addrsz = 4;
got_eof = TRUE;
break;
case 8:
addrsz = 3;
got_eof = TRUE;
break;
case 9:
addrsz = 2;
got_eof = TRUE;
break;
default:
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid srec record type S%c",
line[1]);
return FALSE;
}
/* parse address */
switch (addrsz) {
case 2:
rec_addr32 = dfu_utils_buffer_parse_uint16 (line + 4);
break;
case 3:
rec_addr32 = dfu_utils_buffer_parse_uint24 (line + 4);
break;
case 4:
rec_addr32 = dfu_utils_buffer_parse_uint32 (line + 4);
break;
default:
g_assert_not_reached ();
}
/* header */
if (rec_kind == 0) {
g_autoptr(GString) modname = g_string_new (NULL);
if (rec_addr32 != 0x0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid header record address, got %04x",
rec_addr32);
return FALSE;
}
/* could be anything, lets assume text */
for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) {
guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i);
if (!g_ascii_isgraph (tmp))
break;
g_string_append_c (modname, tmp);
}
if (modname->len != 0)
dfu_image_set_name (image, modname->str);
continue;
}
/* verify we got all records */
if (rec_kind == 5) {
if (rec_addr32 != data_cnt) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"count record was not valid, got 0x%02x expected 0x%02x",
(guint) rec_addr32, (guint) data_cnt);
return FALSE;
}
}
/* data */
if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) {
/* invalid */
if (!got_hdr) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"missing header record");
return FALSE;
}
/* does not make sense */
if (rec_addr32 < addr32_last) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid address 0x%x, last was 0x%x",
(guint) rec_addr32,
(guint) addr32_last);
return FALSE;
}
if (rec_addr32 < start_addr) {
g_debug ("ignoring data at 0x%x as before start address 0x%x",
(guint) rec_addr32, (guint) start_addr);
} else {
guint bytecnt = 0;
guint32 len_hole = rec_addr32 - addr32_last;
/* fill any holes, but only up to 1Mb to avoid a DoS */
if (addr32_last > 0 && len_hole > 0x100000) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"hole of 0x%x bytes too large to fill",
(guint) len_hole);
return FALSE;
}
if (addr32_last > 0x0 && len_hole > 1) {
g_debug ("filling address 0x%08x to 0x%08x",
addr32_last + 1, addr32_last + len_hole - 1);
for (guint j = 0; j < len_hole; j++)
g_string_append_c (outbuf, 0xff);
}
/* add data */
for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) {
guint8 tmp = dfu_utils_buffer_parse_uint8 (line + i);
g_string_append_c (outbuf, tmp);
bytecnt++;
}
if (element_address == 0x0)
element_address = rec_addr32;
addr32_last = rec_addr32 + bytecnt;
}
data_cnt++;
}
}
/* no EOF */
if (!got_eof) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no EOF, perhaps truncated file");
return FALSE;
}
/* add single image */
contents = g_bytes_new (outbuf->str, outbuf->len);
dfu_element_set_contents (element, contents);
dfu_element_set_address (element, element_address);
dfu_image_add_element (image, element);
return TRUE;
}
/** /**
* dfu_firmware_from_srec: (skip) * dfu_firmware_from_srec: (skip)
* @firmware: a #DfuFirmware * @firmware: a #DfuFirmware
@ -331,15 +57,26 @@ dfu_firmware_from_srec (DfuFirmware *firmware,
DfuFirmwareParseFlags flags, DfuFirmwareParseFlags flags,
GError **error) GError **error)
{ {
g_autoptr(DfuImage) image = NULL; g_autoptr(FuFirmware) firmware_new = fu_srec_firmware_new ();
g_autoptr(GPtrArray) imgs = NULL;
FwupdInstallFlags flags_new = FWUPD_INSTALL_FLAG_NONE;
g_return_val_if_fail (bytes != NULL, FALSE); /* make a native objects from the abstract firmware */
if (flags & DFU_FIRMWARE_PARSE_FLAG_NO_CRC_TEST)
/* add single image */ flags_new |= FWUPD_INSTALL_FLAG_FORCE;
image = dfu_image_new (); if (!fu_firmware_parse (firmware_new, bytes, flags_new, error))
if (!dfu_image_from_srec (image, bytes, 0x0, flags, error))
return FALSE; return FALSE;
dfu_firmware_add_image (firmware, image); imgs = fu_firmware_get_images (firmware_new);
for (guint i = 0; i < imgs->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (imgs, i);
g_autoptr(DfuElement) element = dfu_element_new ();
g_autoptr(DfuImage) image = dfu_image_new ();
dfu_element_set_contents (element, fu_firmware_image_get_bytes (img, NULL));
dfu_element_set_address (element, fu_firmware_image_get_addr (img));
dfu_image_add_element (image, element);
dfu_firmware_add_image (firmware, image);
}
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC); dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC);
return TRUE; return TRUE;
} }

View File

@ -20,10 +20,5 @@ gboolean dfu_firmware_from_srec (DfuFirmware *firmware,
GBytes *bytes, GBytes *bytes,
DfuFirmwareParseFlags flags, DfuFirmwareParseFlags flags,
GError **error); GError **error);
gboolean dfu_image_from_srec (DfuImage *image,
GBytes *bytes,
guint32 start_addr,
DfuFirmwareParseFlags flags,
GError **error);
G_END_DECLS G_END_DECLS

View File

@ -320,209 +320,6 @@ dfu_firmware_metadata_func (void)
g_assert_true (ret); g_assert_true (ret);
} }
static void
dfu_firmware_intel_hex_offset_func (void)
{
DfuElement *element_verify;
DfuImage *image_verify;
const guint8 *data;
gboolean ret;
gsize len;
g_autofree gchar *str = NULL;
g_autoptr(DfuElement) element = NULL;
g_autoptr(DfuFirmware) firmware = NULL;
g_autoptr(DfuFirmware) firmware_verify = NULL;
g_autoptr(DfuImage) image = NULL;
g_autoptr(GBytes) data_bin = NULL;
g_autoptr(GBytes) data_dummy = NULL;
g_autoptr(GError) error = NULL;
/* add a 4 byte image in high memory */
element = dfu_element_new ();
data_dummy = g_bytes_new_static ("foo", 4);
dfu_element_set_address (element, 0x80000000);
dfu_element_set_contents (element, data_dummy);
image = dfu_image_new ();
dfu_image_add_element (image, element);
firmware = dfu_firmware_new ();
dfu_firmware_add_image (firmware, image);
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX);
data_bin = dfu_firmware_write_data (firmware, &error);
g_assert_no_error (error);
g_assert (data_bin != NULL);
data = g_bytes_get_data (data_bin, &len);
str = g_strndup ((const gchar *) data, len);
g_assert_cmpstr (str, ==,
":0200000480007A\n"
":04000000666F6F00B8\n"
":00000001FF\n");
/* check we can load it too */
firmware_verify = dfu_firmware_new ();
ret = dfu_firmware_parse_data (firmware_verify, data_bin, DFU_FIRMWARE_PARSE_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
image_verify = dfu_firmware_get_image_default (firmware_verify);
g_assert (image_verify != NULL);
element_verify = dfu_image_get_element_default (image);
g_assert (element_verify != NULL);
g_assert_cmpint (dfu_element_get_address (element_verify), ==, 0x80000000);
g_assert_cmpint (g_bytes_get_size (dfu_element_get_contents (element_verify)), ==, 0x4);
}
static void
dfu_firmware_srec_func (void)
{
gboolean ret;
g_autofree gchar *filename_hex = NULL;
g_autofree gchar *filename_ref = NULL;
g_autoptr(DfuFirmware) firmware = NULL;
g_autoptr(GBytes) data_bin = NULL;
g_autoptr(GBytes) data_ref = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_bin = NULL;
g_autoptr(GFile) file_hex = NULL;
filename_hex = dfu_test_get_filename ("firmware.srec");
g_assert (filename_hex != NULL);
file_hex = g_file_new_for_path (filename_hex);
firmware = dfu_firmware_new ();
ret = dfu_firmware_parse_file (firmware, file_hex,
DFU_FIRMWARE_PARSE_FLAG_NONE,
&error);
g_assert_no_error (error);
g_assert (ret);
g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 136);
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW);
data_bin = dfu_firmware_write_data (firmware, &error);
g_assert_no_error (error);
g_assert (data_bin != NULL);
/* did we match the reference file? */
filename_ref = dfu_test_get_filename ("firmware.bin");
g_assert (filename_ref != NULL);
file_bin = g_file_new_for_path (filename_ref);
data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error);
g_assert_no_error (error);
g_assert (data_ref != NULL);
ret = fu_common_bytes_compare (data_bin, data_ref, &error);
g_assert_no_error (error);
g_assert_true (ret);
}
static void
dfu_firmware_intel_hex_func (void)
{
const guint8 *data;
gboolean ret;
gsize len;
g_autofree gchar *filename_hex = NULL;
g_autofree gchar *filename_ref = NULL;
g_autofree gchar *str = NULL;
g_autoptr(DfuFirmware) firmware = NULL;
g_autoptr(GBytes) data_bin2 = NULL;
g_autoptr(GBytes) data_bin = NULL;
g_autoptr(GBytes) data_hex = NULL;
g_autoptr(GBytes) data_ref = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_bin = NULL;
g_autoptr(GFile) file_hex = NULL;
/* load a Intel hex32 file */
filename_hex = dfu_test_get_filename ("firmware.hex");
g_assert (filename_hex != NULL);
file_hex = g_file_new_for_path (filename_hex);
firmware = dfu_firmware_new ();
ret = dfu_firmware_parse_file (firmware, file_hex,
DFU_FIRMWARE_PARSE_FLAG_NONE,
&error);
g_assert_no_error (error);
g_assert (ret);
g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 136);
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW);
data_bin = dfu_firmware_write_data (firmware, &error);
g_assert_no_error (error);
g_assert (data_bin != NULL);
/* did we match the reference file? */
filename_ref = dfu_test_get_filename ("firmware.bin");
g_assert (filename_ref != NULL);
file_bin = g_file_new_for_path (filename_ref);
data_ref = dfu_self_test_get_bytes_for_file (file_bin, &error);
g_assert_no_error (error);
g_assert (data_ref != NULL);
ret = fu_common_bytes_compare (data_bin, data_ref, &error);
g_assert_no_error (error);
g_assert_true (ret);
/* export a ihex file (which will be slightly different due to
* non-continous regions being expanded */
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_INTEL_HEX);
data_hex = dfu_firmware_write_data (firmware, &error);
g_assert_no_error (error);
g_assert (data_hex != NULL);
data = g_bytes_get_data (data_hex, &len);
str = g_strndup ((const gchar *) data, len);
g_assert_cmpstr (str, ==,
":104000003DEF20F000000000FACF01F0FBCF02F0FE\n"
":10401000E9CF03F0EACF04F0E1CF05F0E2CF06F0FC\n"
":10402000D9CF07F0DACF08F0F3CF09F0F4CF0AF0D8\n"
":10403000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF078\n"
":104040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF68\n"
":104050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFA8\n"
":1040600006C0E2FF05C0E1FF04C0EAFF03C0E9FFAC\n"
":1040700002C0FBFF01C0FAFF11003FEF20F000017A\n"
":0840800042EF20F03DEF20F0BB\n"
":00000001FF\n");
/* do we match the binary file again */
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_RAW);
data_bin2 = dfu_firmware_write_data (firmware, &error);
g_assert_no_error (error);
g_assert (data_bin2 != NULL);
ret = fu_common_bytes_compare (data_bin, data_bin2, &error);
g_assert_no_error (error);
g_assert_true (ret);
}
static void
dfu_firmware_intel_hex_signed_func (void)
{
DfuElement *element;
DfuImage *image;
GBytes *data_sig;
const guint8 *data;
gboolean ret;
gsize len;
g_autofree gchar *filename_hex = NULL;
g_autoptr(DfuFirmware) firmware = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_hex = NULL;
/* load a Intel hex32 file */
filename_hex = dfu_test_get_filename ("firmware.shex");
g_assert (filename_hex != NULL);
file_hex = g_file_new_for_path (filename_hex);
firmware = dfu_firmware_new ();
ret = dfu_firmware_parse_file (firmware, file_hex,
DFU_FIRMWARE_PARSE_FLAG_NONE,
&error);
g_assert_no_error (error);
g_assert (ret);
g_assert_cmpint (dfu_firmware_get_size (firmware), ==, 144);
/* get the signed image element */
image = dfu_firmware_get_image_by_name (firmware, "signature");
g_assert (image != NULL);
element = dfu_image_get_element_default (image);
data_sig = dfu_element_get_contents (element);
g_assert (data_sig != NULL);
data = g_bytes_get_data (data_sig, &len);
g_assert_cmpint (len, ==, 8);
g_assert (data != NULL);
}
static gchar * static gchar *
dfu_target_sectors_to_string (DfuTarget *target) dfu_target_sectors_to_string (DfuTarget *target)
{ {
@ -794,7 +591,6 @@ main (int argc, char **argv)
g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
/* tests go here */ /* tests go here */
g_test_add_func ("/dfu/firmware{srec}", dfu_firmware_srec_func);
g_test_add_func ("/dfu/patch", dfu_patch_func); g_test_add_func ("/dfu/patch", dfu_patch_func);
g_test_add_func ("/dfu/patch{merges}", dfu_patch_merges_func); g_test_add_func ("/dfu/patch{merges}", dfu_patch_merges_func);
g_test_add_func ("/dfu/patch{apply}", dfu_patch_apply_func); g_test_add_func ("/dfu/patch{apply}", dfu_patch_apply_func);
@ -806,9 +602,6 @@ main (int argc, char **argv)
g_test_add_func ("/dfu/firmware{dfuse}", dfu_firmware_dfuse_func); g_test_add_func ("/dfu/firmware{dfuse}", dfu_firmware_dfuse_func);
g_test_add_func ("/dfu/firmware{xdfu}", dfu_firmware_xdfu_func); g_test_add_func ("/dfu/firmware{xdfu}", dfu_firmware_xdfu_func);
g_test_add_func ("/dfu/firmware{metadata}", dfu_firmware_metadata_func); g_test_add_func ("/dfu/firmware{metadata}", dfu_firmware_metadata_func);
g_test_add_func ("/dfu/firmware{intel-hex-offset}", dfu_firmware_intel_hex_offset_func);
g_test_add_func ("/dfu/firmware{intel-hex}", dfu_firmware_intel_hex_func);
g_test_add_func ("/dfu/firmware{intel-hex-signed}", dfu_firmware_intel_hex_signed_func);
return g_test_run (); return g_test_run ();
} }

Binary file not shown.

View File

@ -1 +0,0 @@
deadbeef

View File

@ -348,7 +348,7 @@ fu_ebitdo_device_get_serial (FuEbitdoDevice *self)
static gboolean static gboolean
fu_ebitdo_device_write_firmware (FuDevice *device, fu_ebitdo_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
@ -358,6 +358,7 @@ fu_ebitdo_device_write_firmware (FuDevice *device,
const guint chunk_sz = 32; const guint chunk_sz = 32;
guint32 payload_len; guint32 payload_len;
guint32 serial_new[3]; guint32 serial_new[3];
g_autoptr(GBytes) fw = NULL;
g_autoptr(GError) error_local = NULL; g_autoptr(GError) error_local = NULL;
const guint32 app_key_index[16] = { const guint32 app_key_index[16] = {
0x186976e5, 0xcac67acd, 0x38f27fee, 0x0a4948f1, 0x186976e5, 0xcac67acd, 0x38f27fee, 0x0a4948f1,
@ -408,6 +409,11 @@ fu_ebitdo_device_write_firmware (FuDevice *device,
return FALSE; return FALSE;
} }
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* corrupt */ /* corrupt */
if (g_bytes_get_size (fw) < sizeof (FuEbitdoFirmwareHeader)) { if (g_bytes_get_size (fw) < sizeof (FuEbitdoFirmwareHeader)) {
g_set_error_literal (error, g_set_error_literal (error,

View File

@ -595,11 +595,17 @@ fu_fastboot_device_write_qfil (FuDevice *device, FuArchive* archive, GError **er
static gboolean static gboolean
fu_fastboot_device_write_firmware (FuDevice *device, fu_fastboot_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
g_autoptr(FuArchive) archive = NULL; g_autoptr(FuArchive) archive = NULL;
g_autoptr(GBytes) fw = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* decompress entire archive ahead of time */ /* decompress entire archive ahead of time */
archive = fu_archive_new (fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); archive = fu_archive_new (fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error);

View File

@ -600,15 +600,21 @@ fu_mm_device_write_firmware_qmi_pdc (FuDevice *device, GBytes *fw, GArray **acti
static gboolean static gboolean
fu_mm_device_write_firmware (FuDevice *device, fu_mm_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuMmDevice *self = FU_MM_DEVICE (device); FuMmDevice *self = FU_MM_DEVICE (device);
g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(FuArchive) archive = NULL; g_autoptr(FuArchive) archive = NULL;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) array = NULL; g_autoptr(GPtrArray) array = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* lock device */ /* lock device */
locker = fu_device_locker_new (device, error); locker = fu_device_locker_new (device, error);
if (locker == NULL) if (locker == NULL)

View File

@ -395,16 +395,22 @@ fu_nvme_device_close (FuDevice *device, GError **error)
static gboolean static gboolean
fu_nvme_device_write_firmware (FuDevice *device, fu_nvme_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuNvmeDevice *self = FU_NVME_DEVICE (device); FuNvmeDevice *self = FU_NVME_DEVICE (device);
g_autoptr(GBytes) fw2 = NULL; g_autoptr(GBytes) fw2 = NULL;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
guint64 block_size = self->write_block_size > 0 ? guint64 block_size = self->write_block_size > 0 ?
self->write_block_size : 0x1000; self->write_block_size : 0x1000;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* some vendors provide firmware files whose sizes are not multiples /* some vendors provide firmware files whose sizes are not multiples
* of blksz *and* the device won't accept blocks of different sizes */ * of blksz *and* the device won't accept blocks of different sizes */
if (fu_device_has_custom_flag (device, "force-align")) { if (fu_device_has_custom_flag (device, "force-align")) {

View File

@ -315,13 +315,19 @@ fu_rts54hid_device_close (FuUsbDevice *device, GError **error)
static gboolean static gboolean
fu_rts54hid_device_write_firmware (FuDevice *device, fu_rts54hid_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device); FuRts54HidDevice *self = FU_RTS54HID_DEVICE (device);
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* set MCU to high clock rate for better ISP performance */ /* set MCU to high clock rate for better ISP performance */
if (!fu_rts54hid_device_set_clock_mode (self, TRUE, error)) if (!fu_rts54hid_device_set_clock_mode (self, TRUE, error))
return FALSE; return FALSE;

View File

@ -212,13 +212,19 @@ fu_rts54hid_module_close (FuDevice *device, GError **error)
static gboolean static gboolean
fu_rts54hid_module_write_firmware (FuDevice *module, fu_rts54hid_module_write_firmware (FuDevice *module,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuRts54HidModule *self = FU_RTS54HID_MODULE (module); FuRts54HidModule *self = FU_RTS54HID_MODULE (module);
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* build packets */ /* build packets */
chunks = fu_chunk_array_new_from_bytes (fw, chunks = fu_chunk_array_new_from_bytes (fw,
0x00, /* start addr */ 0x00, /* start addr */

View File

@ -309,13 +309,19 @@ fu_rts54hub_device_close (FuUsbDevice *device, GError **error)
static gboolean static gboolean
fu_rts54hub_device_write_firmware (FuDevice *device, fu_rts54hub_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device); FuRts54HubDevice *self = FU_RTS54HUB_DEVICE (device);
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* enable vendor commands */ /* enable vendor commands */
if (!fu_rts54hub_device_vendor_cmd (self, if (!fu_rts54hub_device_vendor_cmd (self,
FU_RTS54HUB_VENDOR_CMD_STATUS | FU_RTS54HUB_VENDOR_CMD_STATUS |
@ -383,7 +389,7 @@ fu_rts54hub_device_write_firmware (FuDevice *device,
return TRUE; return TRUE;
} }
static GBytes * static FuFirmware *
fu_rts54hub_device_prepare_firmware (FuDevice *device, fu_rts54hub_device_prepare_firmware (FuDevice *device,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
@ -405,7 +411,7 @@ fu_rts54hub_device_prepare_firmware (FuDevice *device,
"firmware needs to be dual bank"); "firmware needs to be dual bank");
return NULL; return NULL;
} }
return g_bytes_ref (fw); return fu_firmware_new_from_bytes (fw);
} }
static void static void

View File

@ -7,11 +7,10 @@
#include "config.h" #include "config.h"
#include <string.h> #include <string.h>
#include <json-glib/json-glib.h>
#include "dfu-firmware.h"
#include "fu-chunk.h" #include "fu-chunk.h"
#include "fu-solokey-device.h" #include "fu-solokey-device.h"
#include "fu-solokey-firmware.h"
struct _FuSolokeyDevice { struct _FuSolokeyDevice {
FuUsbDevice parent_instance; FuUsbDevice parent_instance;
@ -396,30 +395,15 @@ fu_solokey_device_setup (FuDevice *device, GError **error)
return TRUE; return TRUE;
} }
static GByteArray *
_g_base64_decode_to_byte_array (const gchar *text)
{
gsize out_len = 0;
guchar *out = g_base64_decode (text, &out_len);
return g_byte_array_new_take ((guint8 *) out, out_len);
}
static GBytes *
_g_base64_decode_to_bytes (const gchar *text)
{
gsize out_len = 0;
guchar *out = g_base64_decode (text, &out_len);
return g_bytes_new_take ((guint8 *) out, out_len);
}
static gboolean static gboolean
fu_solokey_device_verify (FuSolokeyDevice *self, const gchar *base64, GError **error) fu_solokey_device_verify (FuSolokeyDevice *self, GBytes *fw_sig, GError **error)
{ {
g_autoptr(GByteArray) req = g_byte_array_new (); g_autoptr(GByteArray) req = g_byte_array_new ();
g_autoptr(GByteArray) res = NULL; g_autoptr(GByteArray) res = NULL;
g_autoptr(GByteArray) sig = _g_base64_decode_to_byte_array (base64); g_autoptr(GByteArray) sig = g_byte_array_new ();
fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY); fu_device_set_status (FU_DEVICE (self), FWUPD_STATUS_DEVICE_VERIFY);
g_byte_array_append (sig, g_bytes_get_data (fw_sig, NULL), g_bytes_get_size (fw_sig));
fu_solokey_device_exchange (req, SOLO_BOOTLOADER_DONE, 0x00, sig); fu_solokey_device_exchange (req, SOLO_BOOTLOADER_DONE, 0x00, sig);
res = fu_solokey_device_packet (self, SOLO_BOOTLOADER_HID_CMD_BOOT, req, error); res = fu_solokey_device_packet (self, SOLO_BOOTLOADER_HID_CMD_BOOT, req, error);
if (res == NULL) if (res == NULL)
@ -427,75 +411,42 @@ fu_solokey_device_verify (FuSolokeyDevice *self, const gchar *base64, GError **e
return TRUE; return TRUE;
} }
static FuFirmware *
fu_solokey_device_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(FuFirmware) firmware = fu_solokey_firmware_new ();
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
if (!fu_firmware_parse (firmware, fw, flags, error))
return NULL;
return g_steal_pointer (&firmware);
}
static gboolean static gboolean
fu_solokey_device_write_firmware (FuDevice *device, fu_solokey_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuSolokeyDevice *self = FU_SOLOKEY_DEVICE (device); FuSolokeyDevice *self = FU_SOLOKEY_DEVICE (device);
DfuElement *element; g_autoptr(FuFirmwareImage) img = NULL;
DfuImage *image; g_autoptr(GBytes) fw = NULL;
JsonNode *json_root; g_autoptr(GBytes) fw_sig = NULL;
JsonObject *json_obj;
const gchar *base64;
g_autoptr(DfuFirmware) firmware = dfu_firmware_new ();
g_autoptr(GBytes) fw_ihex = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
g_autoptr(GString) base64_websafe = NULL;
g_autoptr(JsonParser) parser = json_parser_new ();
/* parse JSON */ /* get main image */
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); img = fu_firmware_get_image_by_id (firmware, NULL, error);
if (!json_parser_load_from_data (parser, if (img == NULL)
(const gchar *) g_bytes_get_data (fw, NULL),
(gssize) g_bytes_get_size (fw),
error)) {
g_prefix_error (error, "firmware not in JSON format: ");
return FALSE; return FALSE;
}
json_root = json_parser_get_root (parser);
json_obj = json_node_get_object (json_root);
if (!json_object_has_member (json_obj, "firmware")) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"JSON invalid as has no 'firmware'");
return FALSE;
}
if (!json_object_has_member (json_obj, "signature")) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"JSON invalid as has no 'signature'");
return FALSE;
}
/* decode */
base64 = json_object_get_string_member (json_obj, "firmware");
fw_ihex = _g_base64_decode_to_bytes (base64);
if (!dfu_firmware_parse_data (firmware, fw_ihex, DFU_FIRMWARE_PARSE_FLAG_NONE, error))
return FALSE;
image = dfu_firmware_get_image_default (firmware);
if (image == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"No image data");
return FALSE;
}
element = dfu_image_get_element_default (image);
if (element == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"No element data");
return FALSE;
}
/* build packets */ /* build packets */
chunks = fu_chunk_array_new_from_bytes (dfu_element_get_contents (element), fw = fu_firmware_image_get_bytes (img, error);
dfu_element_get_address (element), if (fw == NULL)
return FALSE;
chunks = fu_chunk_array_new_from_bytes (fw,
fu_firmware_image_get_addr (img),
0x00, /* page_sz */ 0x00, /* page_sz */
2048); 2048);
@ -525,11 +476,10 @@ fu_solokey_device_write_firmware (FuDevice *device,
} }
/* verify the signature and reboot back to runtime */ /* verify the signature and reboot back to runtime */
base64_websafe = g_string_new (json_object_get_string_member (json_obj, "signature")); fw_sig = fu_firmware_get_image_by_id_bytes (firmware, "signature", error);
fu_common_string_replace (base64_websafe, "-", "+"); if (fw_sig == NULL)
fu_common_string_replace (base64_websafe, "_", "/"); return FALSE;
g_string_append (base64_websafe, "=="); return fu_solokey_device_verify (self, fw_sig, error);
return fu_solokey_device_verify (self, base64_websafe->str, error);
} }
static void static void
@ -549,6 +499,7 @@ fu_solokey_device_class_init (FuSolokeyDeviceClass *klass)
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass);
klass_device->write_firmware = fu_solokey_device_write_firmware; klass_device->write_firmware = fu_solokey_device_write_firmware;
klass_device->prepare_firmware = fu_solokey_device_prepare_firmware;
klass_device->setup = fu_solokey_device_setup; klass_device->setup = fu_solokey_device_setup;
klass_usb_device->open = fu_solokey_device_open; klass_usb_device->open = fu_solokey_device_open;
klass_usb_device->close = fu_solokey_device_close; klass_usb_device->close = fu_solokey_device_close;

View File

@ -0,0 +1,112 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <json-glib/json-glib.h>
#include "fu-common.h"
#include "fu-ihex-firmware.h"
#include "fu-solokey-firmware.h"
struct _FuSolokeyFirmware {
FuFirmware parent_instance;
};
G_DEFINE_TYPE (FuSolokeyFirmware, fu_solokey_firmware, FU_TYPE_FIRMWARE)
static GBytes *
_g_base64_decode_to_bytes (const gchar *text)
{
gsize out_len = 0;
guchar *out = g_base64_decode (text, &out_len);
return g_bytes_new_take ((guint8 *) out, out_len);
}
static gboolean
fu_solokey_firmware_parse (FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
JsonNode *json_root;
JsonObject *json_obj;
const gchar *base64;
g_autoptr(FuFirmware) ihex_firmware = fu_ihex_firmware_new ();
g_autoptr(FuFirmwareImage) img = NULL;
g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (NULL);
g_autoptr(GBytes) fw_ihex = NULL;
g_autoptr(GBytes) fw_sig = NULL;
g_autoptr(GString) base64_websafe = NULL;
g_autoptr(JsonParser) parser = json_parser_new ();
/* parse JSON */
if (!json_parser_load_from_data (parser,
(const gchar *) g_bytes_get_data (fw, NULL),
(gssize) g_bytes_get_size (fw),
error)) {
g_prefix_error (error, "firmware not in JSON format: ");
return FALSE;
}
json_root = json_parser_get_root (parser);
json_obj = json_node_get_object (json_root);
if (!json_object_has_member (json_obj, "firmware")) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"JSON invalid as has no 'firmware'");
return FALSE;
}
if (!json_object_has_member (json_obj, "signature")) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"JSON invalid as has no 'signature'");
return FALSE;
}
/* decode */
base64 = json_object_get_string_member (json_obj, "firmware");
fw_ihex = _g_base64_decode_to_bytes (base64);
if (!fu_firmware_parse (ihex_firmware, fw_ihex, flags, error))
return FALSE;
img = fu_firmware_get_image_default (ihex_firmware, error);
if (img == NULL)
return FALSE;
fu_firmware_add_image (firmware, img);
/* signature */
base64_websafe = g_string_new (json_object_get_string_member (json_obj, "signature"));
fu_common_string_replace (base64_websafe, "-", "+");
fu_common_string_replace (base64_websafe, "_", "/");
g_string_append (base64_websafe, "==");
fw_sig = _g_base64_decode_to_bytes (base64_websafe->str);
fu_firmware_image_set_bytes (img_sig, fw_sig);
fu_firmware_image_set_id (img_sig, "signature");
fu_firmware_add_image (firmware, img_sig);
return TRUE;
}
static void
fu_solokey_firmware_init (FuSolokeyFirmware *self)
{
}
static void
fu_solokey_firmware_class_init (FuSolokeyFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
klass_firmware->parse = fu_solokey_firmware_parse;
}
FuFirmware *
fu_solokey_firmware_new (void)
{
return FU_FIRMWARE (g_object_new (FU_TYPE_SOLOKEY_FIRMWARE, NULL));
}

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include "fu-firmware.h"
G_BEGIN_DECLS
#define FU_TYPE_SOLOKEY_FIRMWARE (fu_solokey_firmware_get_type ())
G_DECLARE_FINAL_TYPE (FuSolokeyFirmware, fu_solokey_firmware, FU, SOLOKEY_FIRMWARE, FuFirmware)
FuFirmware *fu_solokey_firmware_new (void);
G_END_DECLS

View File

@ -10,11 +10,11 @@ shared_module('fu_plugin_solokey',
fu_hash, fu_hash,
sources : [ sources : [
'fu-solokey-device.c', 'fu-solokey-device.c',
'fu-solokey-firmware.c',
'fu-plugin-solokey.c', 'fu-plugin-solokey.c',
], ],
include_directories : [ include_directories : [
include_directories('../..'), include_directories('../..'),
include_directories('../dfu'),
include_directories('../../src'), include_directories('../../src'),
include_directories('../../libfwupd'), include_directories('../../libfwupd'),
], ],
@ -22,7 +22,6 @@ shared_module('fu_plugin_solokey',
install_dir: plugin_dir, install_dir: plugin_dir,
link_with : [ link_with : [
libfwupdprivate, libfwupdprivate,
dfu,
], ],
c_args : cargs, c_args : cargs,
dependencies : [ dependencies : [

View File

@ -362,7 +362,7 @@ fu_superio_device_setup (FuDevice *device, GError **error)
return TRUE; return TRUE;
} }
static GBytes * static FuFirmware *
fu_superio_device_prepare_firmware (FuDevice *device, fu_superio_device_prepare_firmware (FuDevice *device,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
@ -378,7 +378,7 @@ fu_superio_device_prepare_firmware (FuDevice *device,
if (memcmp (&buf[off], sig1, sizeof(sig1)) == 0 && if (memcmp (&buf[off], sig1, sizeof(sig1)) == 0 &&
memcmp (&buf[off + 8], sig2, sizeof(sig2)) == 0) { memcmp (&buf[off + 8], sig2, sizeof(sig2)) == 0) {
g_debug ("found signature at 0x%04x", (guint) off); g_debug ("found signature at 0x%04x", (guint) off);
return g_bytes_ref (fw); return fu_firmware_new_from_bytes (fw);
} }
} }
g_set_error_literal (error, g_set_error_literal (error,

View File

@ -591,13 +591,14 @@ fu_superio_it89_device_get_jedec_id (FuSuperioDevice *self, guint8 *id, GError *
static gboolean static gboolean
fu_superio_it89_device_write_firmware (FuDevice *device, fu_superio_it89_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuSuperioDevice *self = FU_SUPERIO_DEVICE (device); FuSuperioDevice *self = FU_SUPERIO_DEVICE (device);
guint8 id[4] = { 0x0 }; guint8 id[4] = { 0x0 };
g_autoptr(GBytes) fw_fixed = NULL; g_autoptr(GBytes) fw_fixed = NULL;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* check JEDEC ID */ /* check JEDEC ID */
@ -618,6 +619,11 @@ fu_superio_it89_device_write_firmware (FuDevice *device,
if (!fu_superio_it89_device_check_eflash (self, error)) if (!fu_superio_it89_device_check_eflash (self, error))
return FALSE; return FALSE;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* disable the mirroring of e-flash */ /* disable the mirroring of e-flash */
if (g_getenv ("FWUPD_SUPERIO_DISABLE_MIRROR") != NULL) { if (g_getenv ("FWUPD_SUPERIO_DISABLE_MIRROR") != NULL) {
fw_fixed = fu_plugin_superio_fix_signature (self, fw, error); fw_fixed = fu_plugin_superio_fix_signature (self, fw, error);

View File

@ -14,12 +14,11 @@ fu_dump_parse (const gchar *filename, GError **error)
gchar *data = NULL; gchar *data = NULL;
gsize len = 0; gsize len = 0;
g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) array = NULL; g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new ();
if (!g_file_get_contents (filename, &data, &len, error)) if (!g_file_get_contents (filename, &data, &len, error))
return FALSE; return FALSE;
blob = g_bytes_new_take (data, len); blob = g_bytes_new_take (data, len);
array = fu_synaprom_firmware_new (blob, error); return fu_firmware_parse (firmware, blob, 0, error);
return array != NULL;
} }
static gboolean static gboolean
@ -28,7 +27,10 @@ fu_dump_generate (const gchar *filename, GError **error)
const gchar *data; const gchar *data;
gsize len = 0; gsize len = 0;
g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) blob = NULL;
blob = fu_synaprom_firmware_generate (); g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new ();
blob = fu_firmware_write (firmware, error);
if (blob == NULL)
return FALSE;
data = g_bytes_get_data (blob, &len); data = g_bytes_get_data (blob, &len);
return g_file_set_contents (filename, data, len, error); return g_file_set_contents (filename, data, len, error);
} }

View File

@ -18,6 +18,7 @@ static void
fu_test_synaprom_firmware_func (void) fu_test_synaprom_firmware_func (void)
{ {
const guint8 *buf; const guint8 *buf;
gboolean ret;
gsize sz = 0; gsize sz = 0;
g_autofree gchar *filename = NULL; g_autofree gchar *filename = NULL;
g_autoptr(FuSynapromDevice) device = fu_synaprom_device_new (NULL); g_autoptr(FuSynapromDevice) device = fu_synaprom_device_new (NULL);
@ -25,7 +26,8 @@ fu_test_synaprom_firmware_func (void)
g_autoptr(GBytes) blob2 = NULL; g_autoptr(GBytes) blob2 = NULL;
g_autoptr(GBytes) fw = NULL; g_autoptr(GBytes) fw = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) firmware = NULL; g_autoptr(FuFirmware) firmware2 = NULL;
g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new ();
filename = fu_test_get_filename (TESTDATADIR, "test.pkg"); filename = fu_test_get_filename (TESTDATADIR, "test.pkg");
g_assert_nonnull (filename); g_assert_nonnull (filename);
@ -36,22 +38,18 @@ fu_test_synaprom_firmware_func (void)
g_assert_cmpint (sz, ==, 294); g_assert_cmpint (sz, ==, 294);
g_assert_cmpint (buf[0], ==, 0x01); g_assert_cmpint (buf[0], ==, 0x01);
g_assert_cmpint (buf[1], ==, 0x00); g_assert_cmpint (buf[1], ==, 0x00);
firmware = fu_synaprom_firmware_new (fw, &error); ret = fu_firmware_parse (firmware, fw, 0, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (firmware); g_assert_true (ret);
/* does not exist */ /* does not exist */
blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, 0, NULL); blob1 = fu_firmware_get_image_by_id_bytes (firmware, "NotGoingToExist", NULL);
g_assert_null (blob1); g_assert_null (blob1);
blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, blob1 = fu_firmware_get_image_by_id_bytes (firmware, "cfg-update-header", NULL);
FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER,
NULL);
g_assert_null (blob1); g_assert_null (blob1);
/* header needs to exist */ /* header needs to exist */
blob1 = fu_synaprom_firmware_get_bytes_by_tag (firmware, blob1 = fu_firmware_get_image_by_id_bytes (firmware, "mfw-update-header", &error);
FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER,
&error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (blob1); g_assert_nonnull (blob1);
buf = g_bytes_get_data (blob1, &sz); buf = g_bytes_get_data (blob1, &sz);
@ -64,8 +62,11 @@ fu_test_synaprom_firmware_func (void)
/* payload needs to exist */ /* payload needs to exist */
fu_synaprom_device_set_version (device, 10, 1, 1234); fu_synaprom_device_set_version (device, 10, 1, 1234);
blob2 = fu_synaprom_device_prepare_fw (FU_DEVICE (device), fw, firmware2 = fu_synaprom_device_prepare_fw (FU_DEVICE (device), fw,
FWUPD_INSTALL_FLAG_NONE, &error); FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert_nonnull (firmware2);
blob2 = fu_firmware_get_image_by_id_bytes (firmware2, "mfw-update-payload", &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (blob2); g_assert_nonnull (blob2);
buf = g_bytes_get_data (blob2, &sz); buf = g_bytes_get_data (blob2, &sz);

View File

@ -112,7 +112,7 @@ fu_synaprom_config_setup (FuDevice *device, GError **error)
return TRUE; return TRUE;
} }
static GBytes * static FuFirmware *
fu_synaprom_config_prepare_firmware (FuDevice *device, fu_synaprom_config_prepare_firmware (FuDevice *device,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
@ -121,20 +121,17 @@ fu_synaprom_config_prepare_firmware (FuDevice *device,
FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device);
FuSynapromFirmwareCfgHeader hdr; FuSynapromFirmwareCfgHeader hdr;
g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) firmware = NULL; g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new ();
guint32 product; guint32 product;
guint32 id1; guint32 id1;
/* parse the firmware */ /* parse the firmware */
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
firmware = fu_synaprom_firmware_new (fw, error); if (!fu_firmware_parse (firmware, fw, flags, error))
if (firmware == NULL)
return NULL; return NULL;
/* check the update header product and version */ /* check the update header product and version */
blob = fu_synaprom_firmware_get_bytes_by_tag (firmware, blob = fu_firmware_get_image_by_id_bytes (firmware, "cfg-update-header", error);
FU_SYNAPROM_FIRMWARE_TAG_CFG_HEADER,
error);
if (blob == NULL) if (blob == NULL)
return NULL; return NULL;
if (g_bytes_get_size (blob) != sizeof(hdr)) { if (g_bytes_get_size (blob) != sizeof(hdr)) {
@ -178,19 +175,24 @@ fu_synaprom_config_prepare_firmware (FuDevice *device,
} }
} }
/* get payload */ /* success */
return fu_synaprom_firmware_get_bytes_by_tag (firmware, return g_steal_pointer (&firmware);
FU_SYNAPROM_FIRMWARE_TAG_CFG_PAYLOAD,
error);
} }
static gboolean static gboolean
fu_synaprom_config_write_firmware (FuDevice *device, fu_synaprom_config_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device); FuSynapromConfig *self = FU_SYNAPROM_CONFIG (device);
g_autoptr(GBytes) fw = NULL;
/* get default image */
fw = fu_firmware_get_image_by_id_bytes (firmware, "cfg-update-payload", error);
if (fw == NULL)
return FALSE;
/* I assume the CFG/MFW difference is detected in the device...*/ /* I assume the CFG/MFW difference is detected in the device...*/
return fu_synaprom_device_write_fw (self->device, fw, error); return fu_synaprom_device_write_fw (self->device, fw, error);
} }

View File

@ -230,7 +230,7 @@ fu_synaprom_device_cmd_download_chunk (FuSynapromDevice *device,
return fu_synaprom_device_cmd_send (device, request, reply, 20000, error); return fu_synaprom_device_cmd_send (device, request, reply, 20000, error);
} }
GBytes * FuFirmware *
fu_synaprom_device_prepare_fw (FuDevice *device, fu_synaprom_device_prepare_fw (FuDevice *device,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
@ -240,18 +240,15 @@ fu_synaprom_device_prepare_fw (FuDevice *device,
FuSynapromFirmwareMfwHeader hdr; FuSynapromFirmwareMfwHeader hdr;
guint32 product; guint32 product;
g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) firmware = NULL; g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new ();
/* parse the firmware */ /* parse the firmware */
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING); fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
firmware = fu_synaprom_firmware_new (fw, error); if (!fu_firmware_parse (firmware, fw, flags, error))
if (firmware == NULL)
return NULL; return NULL;
/* check the update header product and version */ /* check the update header product and version */
blob = fu_synaprom_firmware_get_bytes_by_tag (firmware, blob = fu_firmware_get_image_by_id_bytes (firmware, "mfw-update-header", error);
FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER,
error);
if (blob == NULL) if (blob == NULL)
return NULL; return NULL;
if (g_bytes_get_size (blob) != sizeof(hdr)) { if (g_bytes_get_size (blob) != sizeof(hdr)) {
@ -296,10 +293,8 @@ fu_synaprom_device_prepare_fw (FuDevice *device,
} }
} }
/* get payload */ /* success */
return fu_synaprom_firmware_get_bytes_by_tag (firmware, return g_steal_pointer (&firmware);
FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD,
error);
} }
gboolean gboolean
@ -352,11 +347,18 @@ fu_synaprom_device_write_fw (FuSynapromDevice *self, GBytes *fw, GError **error)
static gboolean static gboolean
fu_synaprom_device_write_firmware (FuDevice *device, fu_synaprom_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuSynapromDevice *self = FU_SYNAPROM_DEVICE (device); FuSynapromDevice *self = FU_SYNAPROM_DEVICE (device);
g_autoptr(GBytes) fw = NULL;
/* get default image */
fw = fu_firmware_get_image_by_id_bytes (firmware, "mfw-update-payload", error);
if (fw == NULL)
return FALSE;
return fu_synaprom_device_write_fw (self, fw, error); return fu_synaprom_device_write_fw (self, fw, error);
} }

View File

@ -37,7 +37,7 @@ void fu_synaprom_device_set_version (FuSynapromDevice *self,
guint8 vmajor, guint8 vmajor,
guint8 vminor, guint8 vminor,
guint32 buildnum); guint32 buildnum);
GBytes *fu_synaprom_device_prepare_fw (FuDevice *device, FuFirmware *fu_synaprom_device_prepare_fw (FuDevice *device,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error); GError **error);

View File

@ -11,27 +11,21 @@
#include "fu-synaprom-firmware.h" #include "fu-synaprom-firmware.h"
struct _FuSynapromFirmware {
FuFirmware parent_instance;
};
G_DEFINE_TYPE (FuSynapromFirmware, fu_synaprom_firmware, FU_TYPE_FIRMWARE)
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
guint16 tag; guint16 tag;
guint32 bufsz; guint32 bufsz;
} FuSynapromFirmwareHdr; } FuSynapromFirmwareHdr;
typedef struct {
guint16 tag;
GBytes *bytes;
} FuSynapromFirmwareItem;
/* use only first 12 bit of 16 bits as tag value */ /* use only first 12 bit of 16 bits as tag value */
#define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0 #define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0
#define FU_SYNAPROM_FIRMWARE_SIGSIZE 0x0100 #define FU_SYNAPROM_FIRMWARE_SIGSIZE 0x0100
static void
fu_synaprom_firmware_item_free (FuSynapromFirmwareItem *item)
{
g_bytes_unref (item->bytes);
g_free (item);
}
static const gchar * static const gchar *
fu_synaprom_firmware_tag_to_string (guint16 tag) fu_synaprom_firmware_tag_to_string (guint16 tag)
{ {
@ -46,18 +40,21 @@ fu_synaprom_firmware_tag_to_string (guint16 tag)
return NULL; return NULL;
} }
GPtrArray * static gboolean
fu_synaprom_firmware_new (GBytes *blob, GError **error) fu_synaprom_firmware_parse (FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{ {
const guint8 *buf; const guint8 *buf;
gsize bufsz = 0; gsize bufsz = 0;
gsize offset = 0; gsize offset = 0;
g_autoptr(GPtrArray) firmware = NULL;
g_return_val_if_fail (blob != NULL, NULL); g_return_val_if_fail (fw != NULL, FALSE);
firmware = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_synaprom_firmware_item_free); buf = g_bytes_get_data (fw, &bufsz);
buf = g_bytes_get_data (blob, &bufsz);
/* 256 byte signature as footer */ /* 256 byte signature as footer */
if (bufsz < FU_SYNAPROM_FIRMWARE_SIGSIZE + sizeof(FuSynapromFirmwareHdr)) { if (bufsz < FU_SYNAPROM_FIRMWARE_SIGSIZE + sizeof(FuSynapromFirmwareHdr)) {
@ -65,7 +62,7 @@ fu_synaprom_firmware_new (GBytes *blob, GError **error)
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_DATA, G_IO_ERROR_INVALID_DATA,
"blob is too small to be firmware"); "blob is too small to be firmware");
return NULL; return FALSE;
} }
bufsz -= FU_SYNAPROM_FIRMWARE_SIGSIZE; bufsz -= FU_SYNAPROM_FIRMWARE_SIGSIZE;
@ -73,19 +70,20 @@ fu_synaprom_firmware_new (GBytes *blob, GError **error)
while (offset != bufsz) { while (offset != bufsz) {
FuSynapromFirmwareHdr header; FuSynapromFirmwareHdr header;
guint32 hdrsz; guint32 hdrsz;
g_autofree FuSynapromFirmwareItem *item = NULL; guint32 tag;
g_autoptr(GBytes) bytes = NULL;
g_autoptr(FuFirmwareImage) img = NULL;
/* verify item header */ /* verify item header */
memcpy (&header, buf, sizeof(header)); memcpy (&header, buf, sizeof(header));
item = g_new0 (FuSynapromFirmwareItem, 1); tag = GUINT16_FROM_LE(header.tag);
item->tag = GUINT16_FROM_LE(header.tag); if (tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) {
if (item->tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) {
g_set_error (error, g_set_error (error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_INVALID_DATA, G_IO_ERROR_INVALID_DATA,
"tag 0x%04x is too large", "tag 0x%04x is too large",
item->tag); tag);
return NULL; return FALSE;
} }
hdrsz = GUINT32_FROM_LE(header.bufsz); hdrsz = GUINT32_FROM_LE(header.bufsz);
offset += sizeof(header) + hdrsz; offset += sizeof(header) + hdrsz;
@ -95,41 +93,29 @@ fu_synaprom_firmware_new (GBytes *blob, GError **error)
G_IO_ERROR_INVALID_DATA, G_IO_ERROR_INVALID_DATA,
"data is corrupted 0x%04x > 0x%04x", "data is corrupted 0x%04x > 0x%04x",
(guint) offset, (guint) bufsz); (guint) offset, (guint) bufsz);
return NULL; return FALSE;
} }
/* move pointer to data */ /* move pointer to data */
buf += sizeof(header); buf += sizeof(header);
item->bytes = g_bytes_new (buf, hdrsz); bytes = g_bytes_new (buf, hdrsz);
g_debug ("adding 0x%04x (%s) with size 0x%04x", g_debug ("adding 0x%04x (%s) with size 0x%04x",
item->tag, tag,
fu_synaprom_firmware_tag_to_string (item->tag), fu_synaprom_firmware_tag_to_string (tag),
hdrsz); hdrsz);
g_ptr_array_add (firmware, g_steal_pointer (&item)); img = fu_firmware_image_new (bytes);
fu_firmware_image_set_idx (img, tag);
fu_firmware_image_set_id (img, fu_synaprom_firmware_tag_to_string (tag));
fu_firmware_add_image (firmware, img);
/* next item */ /* next item */
buf += hdrsz; buf += hdrsz;
} }
return g_steal_pointer (&firmware); return TRUE;
} }
GBytes * static GBytes *
fu_synaprom_firmware_get_bytes_by_tag (GPtrArray *firmware, guint16 tag, GError **error) fu_synaprom_firmware_write (FuFirmware *self, GError **error)
{
for (guint i = 0; i < firmware->len; i++) {
FuSynapromFirmwareItem *item = g_ptr_array_index (firmware, i);
if (item->tag == tag)
return g_bytes_ref (item->bytes);
}
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"no item with tag 0x%04x", tag);
return NULL;
}
GBytes *
fu_synaprom_firmware_generate (void)
{ {
GByteArray *blob = g_byte_array_new (); GByteArray *blob = g_byte_array_new ();
const guint8 data[] = { 'R', 'H' }; const guint8 data[] = { 'R', 'H' };
@ -163,3 +149,22 @@ fu_synaprom_firmware_generate (void)
} }
return g_byte_array_free_to_bytes (blob); return g_byte_array_free_to_bytes (blob);
} }
static void
fu_synaprom_firmware_init (FuSynapromFirmware *self)
{
}
static void
fu_synaprom_firmware_class_init (FuSynapromFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
klass_firmware->parse = fu_synaprom_firmware_parse;
klass_firmware->write = fu_synaprom_firmware_write;
}
FuFirmware *
fu_synaprom_firmware_new (void)
{
return FU_FIRMWARE (g_object_new (FU_TYPE_SYNAPROM_FIRMWARE, NULL));
}

View File

@ -7,7 +7,12 @@
#pragma once #pragma once
#include <glib.h> #include "fu-firmware.h"
G_BEGIN_DECLS
#define FU_TYPE_SYNAPROM_FIRMWARE (fu_synaprom_firmware_get_type ())
G_DECLARE_FINAL_TYPE (FuSynapromFirmware, fu_synaprom_firmware, FU, SYNAPROM_FIRMWARE, FuFirmware)
#define FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER 0x0001 #define FU_SYNAPROM_FIRMWARE_TAG_MFW_HEADER 0x0001
#define FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD 0x0002 #define FU_SYNAPROM_FIRMWARE_TAG_MFW_PAYLOAD 0x0002
@ -34,9 +39,6 @@ typedef struct __attribute__((packed)) {
guint8 unused[2]; guint8 unused[2];
} FuSynapromFirmwareCfgHeader; } FuSynapromFirmwareCfgHeader;
GPtrArray *fu_synaprom_firmware_new (GBytes *blob, FuFirmware *fu_synaprom_firmware_new (void);
GError **error);
GBytes *fu_synaprom_firmware_get_bytes_by_tag (GPtrArray *firmware, G_END_DECLS
guint16 tag,
GError **error);
GBytes *fu_synaprom_firmware_generate (void);

View File

@ -66,10 +66,15 @@ if get_option('tests')
], ],
include_directories : [ include_directories : [
include_directories('../..'), include_directories('../..'),
include_directories('../../src'),
include_directories('../../libfwupd'),
], ],
dependencies : [ dependencies : [
gio, gio,
], ],
link_with : [
libfwupdprivate,
],
c_args : cargs c_args : cargs
) )
endif endif

View File

@ -401,7 +401,7 @@ fu_uefi_device_write_update_info (FuUefiDevice *self,
static gboolean static gboolean
fu_uefi_device_write_firmware (FuDevice *device, fu_uefi_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags install_flags, FwupdInstallFlags install_flags,
GError **error) GError **error)
{ {
@ -411,6 +411,7 @@ fu_uefi_device_write_firmware (FuDevice *device,
const gchar *esp_path = fu_device_get_metadata (device, "EspPath"); const gchar *esp_path = fu_device_get_metadata (device, "EspPath");
efi_guid_t guid; efi_guid_t guid;
g_autoptr(GBytes) fixed_fw = NULL; g_autoptr(GBytes) fixed_fw = NULL;
g_autoptr(GBytes) fw = NULL;
g_autofree gchar *basename = NULL; g_autofree gchar *basename = NULL;
g_autofree gchar *directory = NULL; g_autofree gchar *directory = NULL;
g_autofree gchar *fn = NULL; g_autofree gchar *fn = NULL;
@ -425,6 +426,11 @@ fu_uefi_device_write_firmware (FuDevice *device,
return FALSE; return FALSE;
} }
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* save the blob to the ESP */ /* save the blob to the ESP */
directory = fu_uefi_get_esp_path_for_os (esp_path); directory = fu_uefi_get_esp_path_for_os (esp_path);
basename = g_strdup_printf ("fwupd-%s.cap", self->fw_class); basename = g_strdup_printf ("fwupd-%s.cap", self->fw_class);

View File

@ -196,15 +196,21 @@ fu_unifying_bootloader_nordic_erase (FuUnifyingBootloader *self, guint16 addr, G
static gboolean static gboolean
fu_unifying_bootloader_nordic_write_firmware (FuDevice *device, fu_unifying_bootloader_nordic_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device);
const FuUnifyingBootloaderRequest *payload; const FuUnifyingBootloaderRequest *payload;
guint16 addr; guint16 addr;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) reqs = NULL; g_autoptr(GPtrArray) reqs = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* erase firmware pages up to the bootloader */ /* erase firmware pages up to the bootloader */
fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE);
for (addr = fu_unifying_bootloader_get_addr_lo (self); for (addr = fu_unifying_bootloader_get_addr_lo (self);

View File

@ -109,15 +109,21 @@ fu_unifying_bootloader_texas_clear_ram_buffer (FuUnifyingBootloader *self, GErro
static gboolean static gboolean
fu_unifying_bootloader_texas_write_firmware (FuDevice *device, fu_unifying_bootloader_texas_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device); FuUnifyingBootloader *self = FU_UNIFYING_BOOTLOADER (device);
const FuUnifyingBootloaderRequest *payload; const FuUnifyingBootloaderRequest *payload;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) reqs = NULL; g_autoptr(GPtrArray) reqs = NULL;
g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new (); g_autoptr(FuUnifyingBootloaderRequest) req = fu_unifying_bootloader_request_new ();
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* transfer payload */ /* transfer payload */
reqs = fu_unifying_bootloader_parse_requests (self, fw, error); reqs = fu_unifying_bootloader_parse_requests (self, fw, error);
if (reqs == NULL) if (reqs == NULL)

View File

@ -882,7 +882,7 @@ fu_unifying_peripheral_write_firmware_pkt (FuUnifyingPeripheral *self,
static gboolean static gboolean
fu_unifying_peripheral_write_firmware (FuDevice *device, fu_unifying_peripheral_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
@ -891,6 +891,7 @@ fu_unifying_peripheral_write_firmware (FuDevice *device,
const guint8 *data; const guint8 *data;
guint8 cmd = 0x04; guint8 cmd = 0x04;
guint8 idx; guint8 idx;
g_autoptr(GBytes) fw = NULL;
/* if we're in bootloader mode, we should be able to get this feature */ /* if we're in bootloader mode, we should be able to get this feature */
idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU); idx = fu_unifying_peripheral_feature_get_idx (self, HIDPP_FEATURE_DFU);
@ -902,6 +903,11 @@ fu_unifying_peripheral_write_firmware (FuDevice *device,
return FALSE; return FALSE;
} }
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* flash hardware */ /* flash hardware */
data = g_bytes_get_data (fw, &sz); data = g_bytes_get_data (fw, &sz);
fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE);

View File

@ -14,9 +14,9 @@
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include "fu-chunk.h" #include "fu-chunk.h"
#include "fu-ihex-firmware.h"
#include "fu-wacom-common.h" #include "fu-wacom-common.h"
#include "fu-wacom-device.h" #include "fu-wacom-device.h"
#include "dfu-firmware.h"
typedef struct typedef struct
{ {
@ -216,69 +216,56 @@ fu_wacom_device_set_version_bootloader (FuWacomDevice *self, GError **error)
return TRUE; return TRUE;
} }
static FuFirmware *
fu_wacom_device_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new ();
if (!fu_firmware_parse (firmware, fw, flags, error))
return NULL;
return g_steal_pointer (&firmware);
}
static gboolean static gboolean
fu_wacom_device_write_firmware (FuDevice *device, fu_wacom_device_write_firmware (FuDevice *device,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
FuWacomDevice *self = FU_WACOM_DEVICE (device); FuWacomDevice *self = FU_WACOM_DEVICE (device);
FuWacomDevicePrivate *priv = GET_PRIVATE (self); FuWacomDevicePrivate *priv = GET_PRIVATE (self);
FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS (device); FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS (device);
DfuElement *element; g_autoptr(FuFirmwareImage) img = NULL;
DfuImage *image; g_autoptr(GBytes) fw = NULL;
GBytes *fw_new;
g_autoptr(DfuFirmware) firmware = dfu_firmware_new ();
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
/* parse hex file */
if (!dfu_firmware_parse_data (firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, error))
return FALSE;
if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"expected firmware format is 'ihex', got '%s'",
dfu_firmware_format_to_string (dfu_firmware_get_format (firmware)));
return FALSE;
}
/* use the correct image from the firmware */ /* use the correct image from the firmware */
image = dfu_firmware_get_image_default (firmware); img = fu_firmware_get_image_default (firmware, error);
if (image == NULL) { if (img == NULL)
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no firmware image");
return FALSE; return FALSE;
}
element = dfu_image_get_element_default (image);
if (element == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no element in image");
return FALSE;
}
g_debug ("using element at addr 0x%0x", g_debug ("using element at addr 0x%0x",
(guint) dfu_element_get_address (element)); (guint) fu_firmware_image_get_addr (img));
/* check start address and size */ /* check start address and size */
if (dfu_element_get_address (element) != priv->flash_base_addr) { if (fu_firmware_image_get_addr (img) != priv->flash_base_addr) {
g_set_error (error, g_set_error (error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_FAILED, G_IO_ERROR_FAILED,
"base addr invalid: 0x%05x", "base addr invalid: 0x%05x",
(guint) dfu_element_get_address (element)); (guint) fu_firmware_image_get_addr (img));
return FALSE; return FALSE;
} }
fw_new = dfu_element_get_contents (element); fw = fu_firmware_image_get_bytes (img, error);
if (g_bytes_get_size (fw_new) > priv->flash_size) { if (fw == NULL)
return FALSE;
if (g_bytes_get_size (fw) > priv->flash_size) {
g_set_error (error, g_set_error (error,
G_IO_ERROR, G_IO_ERROR,
G_IO_ERROR_FAILED, G_IO_ERROR_FAILED,
"size is invalid: 0x%05x", "size is invalid: 0x%05x",
(guint) g_bytes_get_size (fw_new)); (guint) g_bytes_get_size (fw));
return FALSE; return FALSE;
} }
@ -289,7 +276,7 @@ fu_wacom_device_write_firmware (FuDevice *device,
return FALSE; return FALSE;
/* flash chunks */ /* flash chunks */
chunks = fu_chunk_array_new_from_bytes (fw_new, priv->flash_base_addr, chunks = fu_chunk_array_new_from_bytes (fw, priv->flash_base_addr,
0x00, /* page_sz */ 0x00, /* page_sz */
priv->flash_block_size); priv->flash_block_size);
return klass->write_firmware (device, chunks, error); return klass->write_firmware (device, chunks, error);
@ -415,6 +402,7 @@ fu_wacom_device_class_init (FuWacomDeviceClass *klass)
klass_device->to_string = fu_wacom_device_to_string; klass_device->to_string = fu_wacom_device_to_string;
klass_device->open = fu_wacom_device_open; klass_device->open = fu_wacom_device_open;
klass_device->close = fu_wacom_device_close; klass_device->close = fu_wacom_device_close;
klass_device->prepare_firmware = fu_wacom_device_prepare_firmware;
klass_device->write_firmware = fu_wacom_device_write_firmware; klass_device->write_firmware = fu_wacom_device_write_firmware;
klass_device->attach = fu_wacom_device_attach; klass_device->attach = fu_wacom_device_attach;
klass_device->detach = fu_wacom_device_detach; klass_device->detach = fu_wacom_device_detach;

View File

@ -8,7 +8,6 @@
#include "fu-wacom-common.h" #include "fu-wacom-common.h"
#include "fu-udev-device.h" #include "fu-udev-device.h"
#include "dfu-element.h"
G_BEGIN_DECLS G_BEGIN_DECLS

View File

@ -15,7 +15,6 @@ shared_module('fu_plugin_wacom_raw',
], ],
include_directories : [ include_directories : [
include_directories('../..'), include_directories('../..'),
include_directories('../dfu'),
include_directories('../../src'), include_directories('../../src'),
include_directories('../../libfwupd'), include_directories('../../libfwupd'),
], ],
@ -26,6 +25,6 @@ shared_module('fu_plugin_wacom_raw',
plugin_deps, plugin_deps,
], ],
link_with : [ link_with : [
dfu, libfwupdprivate,
], ],
) )

View File

@ -19,11 +19,10 @@
static void static void
fu_wac_firmware_parse_func (void) fu_wac_firmware_parse_func (void)
{ {
DfuElement *element;
DfuImage *image;
gboolean ret; gboolean ret;
g_autofree gchar *fn = NULL; g_autofree gchar *fn = NULL;
g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); g_autoptr(FuFirmware) firmware = fu_wac_firmware_new ();
g_autoptr(FuFirmwareImage) img = NULL;
g_autoptr(GBytes) blob_block = NULL; g_autoptr(GBytes) blob_block = NULL;
g_autoptr(GBytes) bytes = NULL; g_autoptr(GBytes) bytes = NULL;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
@ -37,20 +36,17 @@ fu_wac_firmware_parse_func (void)
bytes = fu_common_get_contents_bytes (fn, &error); bytes = fu_common_get_contents_bytes (fn, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (bytes); g_assert_nonnull (bytes);
ret = fu_wac_firmware_parse_data (firmware, bytes, ret = fu_firmware_parse (firmware, bytes, FWUPD_INSTALL_FLAG_NONE, &error);
DFU_FIRMWARE_PARSE_FLAG_NONE, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_true (ret); g_assert_true (ret);
/* get image data */ /* get image data */
image = dfu_firmware_get_image (firmware, 0); img = fu_firmware_get_image_default (firmware, &error);
g_assert_nonnull (image); g_assert_no_error (error);
element = dfu_image_get_element_default (image); g_assert_nonnull (img);
g_assert_nonnull (element);
/* get block */ /* get block */
blob_block = dfu_element_get_contents_chunk (element, 0x8008000, blob_block = fu_firmware_image_get_bytes_chunk (img, 0x8008000, 1024, &error);
1024, &error);
g_assert_no_error (error); g_assert_no_error (error);
g_assert_nonnull (blob_block); g_assert_nonnull (blob_block);
fu_wac_buffer_dump ("IMG", FU_WAC_REPORT_ID_MODULE, fu_wac_buffer_dump ("IMG", FU_WAC_REPORT_ID_MODULE,

View File

@ -15,9 +15,6 @@
#include "fu-wac-module-bluetooth.h" #include "fu-wac-module-bluetooth.h"
#include "fu-wac-module-touch.h" #include "fu-wac-module-touch.h"
#include "dfu-common.h"
#include "dfu-firmware.h"
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
guint32 start_addr; guint32 start_addr;
guint32 block_sz; guint32 block_sz;
@ -481,34 +478,37 @@ fu_wac_device_switch_to_flash_loader (FuWacDevice *self, GError **error)
error); error);
} }
static FuFirmware *
fu_wac_device_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(FuFirmware) firmware = fu_wac_firmware_new ();
fu_device_set_status (device, FWUPD_STATUS_DECOMPRESSING);
if (!fu_firmware_parse (firmware, fw, flags, error))
return NULL;
return g_steal_pointer (&firmware);
}
static gboolean static gboolean
fu_wac_device_write_firmware (FuDevice *device, fu_wac_device_write_firmware (FuDevice *device,
GBytes *blob, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
DfuElement *element;
DfuImage *image;
FuWacDevice *self = FU_WAC_DEVICE (device); FuWacDevice *self = FU_WAC_DEVICE (device);
gsize blocks_done = 0; gsize blocks_done = 0;
gsize blocks_total = 0; gsize blocks_total = 0;
g_autoptr(DfuFirmware) firmware = dfu_firmware_new ();
g_autoptr(GHashTable) fd_blobs = NULL;
g_autofree guint32 *csum_local = NULL; g_autofree guint32 *csum_local = NULL;
g_autoptr(FuFirmwareImage) img = NULL;
g_autoptr(GHashTable) fd_blobs = NULL;
/* load .wac file, including metadata */ /* use the correct image from the firmware */
if (!fu_wac_firmware_parse_data (firmware, blob, img = fu_firmware_get_image_by_idx (firmware, self->firmware_index == 1 ? 1 : 0, error);
DFU_FIRMWARE_PARSE_FLAG_NONE, if (img == NULL)
error))
return FALSE; return FALSE;
if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_SREC) { g_debug ("using image at addr 0x%0x", (guint) fu_firmware_image_get_addr (img));
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"expected firmware format is 'srec', got '%s'",
dfu_firmware_format_to_string (dfu_firmware_get_format (firmware)));
return FALSE;
}
/* enter flash mode */ /* enter flash mode */
if (!fu_wac_device_switch_to_flash_loader (self, error)) if (!fu_wac_device_switch_to_flash_loader (self, error))
@ -518,28 +518,6 @@ fu_wac_device_write_firmware (FuDevice *device,
if (!fu_wac_device_ensure_firmware_index (self, error)) if (!fu_wac_device_ensure_firmware_index (self, error))
return FALSE; return FALSE;
/* use the correct image from the firmware */
image = dfu_firmware_get_image (firmware, self->firmware_index == 1 ? 1 : 0);
if (image == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no firmware image for index %" G_GUINT16_FORMAT,
self->firmware_index);
return FALSE;
}
element = dfu_image_get_element_default (image);
if (element == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no element in image %" G_GUINT16_FORMAT,
self->firmware_index);
return FALSE;
}
g_debug ("using element at addr 0x%0x",
(guint) dfu_element_get_address (element));
/* get firmware parameters (page sz and transfer sz) */ /* get firmware parameters (page sz and transfer sz) */
if (!fu_wac_device_ensure_parameters (self, error)) if (!fu_wac_device_ensure_parameters (self, error))
return FALSE; return FALSE;
@ -572,13 +550,13 @@ fu_wac_device_write_firmware (FuDevice *device,
if (fu_wav_device_flash_descriptor_is_wp (fd)) if (fu_wav_device_flash_descriptor_is_wp (fd))
continue; continue;
blob_tmp = dfu_element_get_contents_chunk (element, blob_tmp = fu_firmware_image_get_bytes_chunk (img,
fd->start_addr, fd->start_addr,
fd->block_sz, fd->block_sz,
NULL); NULL);
if (blob_tmp == NULL) if (blob_tmp == NULL)
break; break;
blob_block = dfu_utils_bytes_pad (blob_tmp, fd->block_sz); blob_block = fu_common_bytes_pad (blob_tmp, fd->block_sz);
g_hash_table_insert (fd_blobs, fd, blob_block); g_hash_table_insert (fd_blobs, fd, blob_block);
} }
@ -898,6 +876,7 @@ fu_wac_device_class_init (FuWacDeviceClass *klass)
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass); FuUsbDeviceClass *klass_usb_device = FU_USB_DEVICE_CLASS (klass);
object_class->finalize = fu_wac_device_finalize; object_class->finalize = fu_wac_device_finalize;
klass_device->prepare_firmware = fu_wac_device_prepare_firmware;
klass_device->write_firmware = fu_wac_device_write_firmware; klass_device->write_firmware = fu_wac_device_write_firmware;
klass_device->to_string = fu_wac_device_to_string; klass_device->to_string = fu_wac_device_to_string;
klass_device->setup = fu_wac_device_setup; klass_device->setup = fu_wac_device_setup;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com> * Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
* *
* SPDX-License-Identifier: LGPL-2.1+ * SPDX-License-Identifier: LGPL-2.1+
*/ */
@ -8,47 +8,42 @@
#include <string.h> #include <string.h>
#include "dfu-element.h" #include "fu-common.h"
#include "dfu-format-srec.h" #include "fu-srec-firmware.h"
#include "dfu-image.h" #include "fu-firmware-common.h"
#include "fu-wac-firmware.h" #include "fu-wac-firmware.h"
#include "fwupd-error.h" #include "fwupd-error.h"
struct _FuWacFirmware {
FuFirmware parent_instance;
};
G_DEFINE_TYPE (FuWacFirmware, fu_wac_firmware, FU_TYPE_FIRMWARE)
typedef struct { typedef struct {
guint32 addr; guint32 addr;
guint32 sz; guint32 sz;
guint32 prog_start_addr; guint32 prog_start_addr;
} DfuFirmwareWacHeaderRecord; } FuFirmwareWacHeaderRecord;
/** static gboolean
* fu_wac_firmware_parse_data: fu_wac_firmware_parse (FuFirmware *firmware,
* @firmware: a #DfuFirmware GBytes *fw,
* @bytes: data to parse guint64 addr_start,
* @flags: some #DfuFirmwareParseFlags guint64 addr_end,
* @error: a #GError, or %NULL FwupdInstallFlags flags,
* GError **error)
* Unpacks into a firmware object from DfuSe data.
*
* Returns: %TRUE for success
**/
gboolean
fu_wac_firmware_parse_data (DfuFirmware *firmware,
GBytes *bytes,
DfuFirmwareParseFlags flags,
GError **error)
{ {
gsize len; gsize len;
guint8 *data; guint8 *data;
g_auto(GStrv) lines = NULL;
g_autoptr(GString) image_buffer = NULL;
g_autofree gchar *data_str = NULL;
guint8 images_cnt = 0; guint8 images_cnt = 0;
g_auto(GStrv) lines = NULL;
g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func (g_free); g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func (g_free);
g_autoptr(GString) image_buffer = NULL;
/* check the prefix (BE) */ /* check the prefix (BE) */
data = (guint8 *) g_bytes_get_data (bytes, &len); data = (guint8 *) g_bytes_get_data (fw, &len);
if (memcmp (data, "WACOM", 5) != 0) { if (memcmp (data, "WACOM", 5) != 0) {
g_set_error_literal (error, g_set_error_literal (error,
FWUPD_ERROR, FWUPD_ERROR,
@ -58,8 +53,7 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
} }
/* parse each line */ /* parse each line */
data_str = g_strndup ((const gchar *) data, len); lines = fu_common_strnsplit ((const gchar *) data, len, "\n", -1);
lines = g_strsplit (data_str, "\n", -1);
for (guint i = 0; lines[i] != NULL; i++) { for (guint i = 0; lines[i] != NULL; i++) {
g_autofree gchar *cmd = g_strndup (lines[i], 2); g_autofree gchar *cmd = g_strndup (lines[i], 2);
@ -81,11 +75,11 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
cmdlen); cmdlen);
return FALSE; return FALSE;
} }
header_image_cnt = dfu_utils_buffer_parse_uint4 (lines[i] + 5); header_image_cnt = fu_firmware_strparse_uint4 (lines[i] + 5);
for (guint j = 0; j < header_image_cnt; j++) { for (guint j = 0; j < header_image_cnt; j++) {
DfuFirmwareWacHeaderRecord *hdr = g_new0 (DfuFirmwareWacHeaderRecord, 1); FuFirmwareWacHeaderRecord *hdr = g_new0 (FuFirmwareWacHeaderRecord, 1);
hdr->addr = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 6); hdr->addr = fu_firmware_strparse_uint32 (lines[i] + (j * 16) + 6);
hdr->sz = dfu_utils_buffer_parse_uint32 (lines[i] + (j * 16) + 14); hdr->sz = fu_firmware_strparse_uint32 (lines[i] + (j * 16) + 14);
g_ptr_array_add (header_infos, hdr); g_ptr_array_add (header_infos, hdr);
g_debug ("header_fw%u_addr: 0x%x", j, hdr->addr); g_debug ("header_fw%u_addr: 0x%x", j, hdr->addr);
g_debug ("header_fw%u_sz: 0x%x", j, hdr->sz); g_debug ("header_fw%u_sz: 0x%x", j, hdr->sz);
@ -95,8 +89,8 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
/* firmware headline record */ /* firmware headline record */
if (cmdlen == 13) { if (cmdlen == 13) {
DfuFirmwareWacHeaderRecord *hdr; FuFirmwareWacHeaderRecord *hdr;
guint8 idx = dfu_utils_buffer_parse_uint4 (lines[i] + 2); guint8 idx = fu_firmware_strparse_uint4 (lines[i] + 2);
if (idx == 0) { if (idx == 0) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
@ -114,7 +108,7 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
return FALSE; return FALSE;
} }
hdr = g_ptr_array_index (header_infos, idx - 1); hdr = g_ptr_array_index (header_infos, idx - 1);
hdr->prog_start_addr = dfu_utils_buffer_parse_uint32 (lines[i] + 3); hdr->prog_start_addr = fu_firmware_strparse_uint32 (lines[i] + 3);
if (hdr->prog_start_addr != hdr->addr) { if (hdr->prog_start_addr != hdr->addr) {
g_set_error (error, g_set_error (error,
FWUPD_ERROR, FWUPD_ERROR,
@ -168,8 +162,9 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
/* end */ /* end */
if (g_strcmp0 (cmd, "S7") == 0) { if (g_strcmp0 (cmd, "S7") == 0) {
g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) blob = NULL;
g_autoptr(DfuImage) image = dfu_image_new (); g_autoptr(FuFirmware) firmware_srec = fu_srec_firmware_new ();
DfuFirmwareWacHeaderRecord *hdr; g_autoptr(FuFirmwareImage) img = NULL;
FuFirmwareWacHeaderRecord *hdr;
/* get the correct relocated start address */ /* get the correct relocated start address */
if (images_cnt >= header_infos->len) { if (images_cnt >= header_infos->len) {
@ -191,12 +186,13 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
/* parse SREC file and add as image */ /* parse SREC file and add as image */
blob = g_bytes_new (image_buffer->str, image_buffer->len); blob = g_bytes_new (image_buffer->str, image_buffer->len);
if (!dfu_image_from_srec (image, blob, hdr->addr, flags, error)) if (!fu_firmware_parse_full (firmware_srec, blob, hdr->addr, 0x0, flags, error))
return FALSE; return FALSE;
img = fu_firmware_get_image_default (firmware_srec, error);
/* the alt-setting is used for the firmware index */ if (img == NULL)
dfu_image_set_alt_setting (image, images_cnt); return FALSE;
dfu_firmware_add_image (firmware, image); fu_firmware_image_set_idx (img, images_cnt);
fu_firmware_add_image (firmware, img);
images_cnt++; images_cnt++;
/* clear the image buffer */ /* clear the image buffer */
@ -226,6 +222,23 @@ fu_wac_firmware_parse_data (DfuFirmware *firmware,
} }
/* success */ /* success */
dfu_firmware_set_format (firmware, DFU_FIRMWARE_FORMAT_SREC);
return TRUE; return TRUE;
} }
static void
fu_wac_firmware_init (FuWacFirmware *self)
{
}
static void
fu_wac_firmware_class_init (FuWacFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
klass_firmware->parse = fu_wac_firmware_parse;
}
FuFirmware *
fu_wac_firmware_new (void)
{
return FU_FIRMWARE (g_object_new (FU_TYPE_WAC_FIRMWARE, NULL));
}

View File

@ -1,20 +1,18 @@
/* /*
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com> * Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
* *
* SPDX-License-Identifier: LGPL-2.1+ * SPDX-License-Identifier: LGPL-2.1+
*/ */
#pragma once #pragma once
#include <gio/gio.h> #include "fu-firmware.h"
#include "dfu-firmware.h"
G_BEGIN_DECLS G_BEGIN_DECLS
gboolean fu_wac_firmware_parse_data (DfuFirmware *firmware, #define FU_TYPE_WAC_FIRMWARE (fu_wac_firmware_get_type ())
GBytes *bytes, G_DECLARE_FINAL_TYPE (FuWacFirmware, fu_wac_firmware, FU, WAC_FIRMWARE, FuFirmware)
DfuFirmwareParseFlags flags,
GError **error); FuFirmware *fu_wac_firmware_new (void);
G_END_DECLS G_END_DECLS

View File

@ -106,7 +106,7 @@ fu_wac_module_bluetooth_parse_blocks (const guint8 *data, gsize sz, gboolean ski
static gboolean static gboolean
fu_wac_module_bluetooth_write_firmware (FuDevice *device, fu_wac_module_bluetooth_write_firmware (FuDevice *device,
GBytes *blob, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
@ -117,9 +117,15 @@ fu_wac_module_bluetooth_write_firmware (FuDevice *device,
const guint8 buf_start[] = { 0x00 }; const guint8 buf_start[] = { 0x00 };
g_autoptr(GPtrArray) blocks = NULL; g_autoptr(GPtrArray) blocks = NULL;
g_autoptr(GBytes) blob_start = g_bytes_new_static (buf_start, 1); g_autoptr(GBytes) blob_start = g_bytes_new_static (buf_start, 1);
g_autoptr(GBytes) fw = NULL;
/* get default image */
fw = fu_firmware_get_image_default_bytes (firmware, error);
if (fw == NULL)
return FALSE;
/* build each data packet */ /* build each data packet */
data = g_bytes_get_data (blob, &len); data = g_bytes_get_data (fw, &len);
blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE); blocks = fu_wac_module_bluetooth_parse_blocks (data, len, TRUE);
blocks_total = blocks->len + 2; blocks_total = blocks->len + 2;

View File

@ -12,7 +12,7 @@
#include "fu-wac-module-touch.h" #include "fu-wac-module-touch.h"
#include "fu-chunk.h" #include "fu-chunk.h"
#include "dfu-firmware.h" #include "fu-ihex-firmware.h"
struct _FuWacModuleTouch struct _FuWacModuleTouch
{ {
@ -21,57 +21,43 @@ struct _FuWacModuleTouch
G_DEFINE_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU_TYPE_WAC_MODULE) G_DEFINE_TYPE (FuWacModuleTouch, fu_wac_module_touch, FU_TYPE_WAC_MODULE)
static FuFirmware *
fu_wac_module_touch_prepare_firmware (FuDevice *device,
GBytes *fw,
FwupdInstallFlags flags,
GError **error)
{
g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new ();
if (!fu_firmware_parse (firmware, fw, flags, error))
return NULL;
return g_steal_pointer (&firmware);
}
static gboolean static gboolean
fu_wac_module_touch_write_firmware (FuDevice *device, fu_wac_module_touch_write_firmware (FuDevice *device,
GBytes *blob, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error) GError **error)
{ {
DfuElement *element;
DfuImage *image;
FuWacModule *self = FU_WAC_MODULE (device); FuWacModule *self = FU_WAC_MODULE (device);
gsize blocks_total = 0; gsize blocks_total = 0;
g_autoptr(FuFirmwareImage) img = NULL;
g_autoptr(GBytes) fw = NULL;
g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GPtrArray) chunks = NULL;
g_autoptr(DfuFirmware) firmware = dfu_firmware_new ();
/* load .hex file */
if (!dfu_firmware_parse_data (firmware, blob, DFU_FIRMWARE_PARSE_FLAG_NONE, error))
return FALSE;
/* check type */
if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"expected firmware format is 'ihex', got '%s'",
dfu_firmware_format_to_string (dfu_firmware_get_format (firmware)));
return FALSE;
}
/* use the correct image from the firmware */ /* use the correct image from the firmware */
image = dfu_firmware_get_image (firmware, 0); img = fu_firmware_get_image_default (firmware, error);
if (image == NULL) { if (img == NULL)
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no firmware image");
return FALSE; return FALSE;
}
element = dfu_image_get_element_default (image);
if (element == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"no firmware element");
return FALSE;
}
g_debug ("using element at addr 0x%0x", g_debug ("using element at addr 0x%0x",
(guint) dfu_element_get_address (element)); (guint) fu_firmware_image_get_addr (img));
blob = dfu_element_get_contents (element); fw = fu_firmware_image_get_bytes (img, error);
if (fw == NULL)
return FALSE;
/* build each data packet */ /* build each data packet */
chunks = fu_chunk_array_new_from_bytes (blob, chunks = fu_chunk_array_new_from_bytes (fw,
dfu_element_get_address (element), fu_firmware_image_get_addr (img),
0x0, /* page_sz */ 0x0, /* page_sz */
128); /* packet_sz */ 128); /* packet_sz */
blocks_total = chunks->len + 2; blocks_total = chunks->len + 2;
@ -129,6 +115,7 @@ static void
fu_wac_module_touch_class_init (FuWacModuleTouchClass *klass) fu_wac_module_touch_class_init (FuWacModuleTouchClass *klass)
{ {
FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass);
klass_device->prepare_firmware = fu_wac_module_touch_prepare_firmware;
klass_device->write_firmware = fu_wac_module_touch_write_firmware; klass_device->write_firmware = fu_wac_module_touch_write_firmware;
} }

View File

@ -12,9 +12,6 @@
#include "fu-wac-common.h" #include "fu-wac-common.h"
#include "fu-wac-device.h" #include "fu-wac-device.h"
#include "dfu-common.h"
#include "dfu-firmware.h"
#define FU_WAC_MODULE_STATUS_OK 0 #define FU_WAC_MODULE_STATUS_OK 0
#define FU_WAC_MODULE_STATUS_BUSY 1 #define FU_WAC_MODULE_STATUS_BUSY 1
#define FU_WAC_MODULE_STATUS_ERR_CRC 2 #define FU_WAC_MODULE_STATUS_ERR_CRC 2

View File

@ -17,7 +17,6 @@ shared_module('fu_plugin_wacom_usb',
], ],
include_directories : [ include_directories : [
include_directories('../..'), include_directories('../..'),
include_directories('../dfu'),
include_directories('../../src'), include_directories('../../src'),
include_directories('../../libfwupd'), include_directories('../../libfwupd'),
], ],
@ -29,7 +28,6 @@ shared_module('fu_plugin_wacom_usb',
], ],
link_with : [ link_with : [
libfwupdprivate, libfwupdprivate,
dfu,
], ],
) )
@ -46,7 +44,6 @@ if get_option('tests')
], ],
include_directories : [ include_directories : [
include_directories('..'), include_directories('..'),
include_directories('../dfu'),
include_directories('../..'), include_directories('../..'),
include_directories('../../libfwupd'), include_directories('../../libfwupd'),
include_directories('../../src'), include_directories('../../src'),
@ -59,7 +56,6 @@ if get_option('tests')
libm, libm,
], ],
link_with : [ link_with : [
dfu,
libfwupdprivate, libfwupdprivate,
], ],
c_args : cargs c_args : cargs

View File

@ -1333,6 +1333,36 @@ fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
return TRUE; return TRUE;
} }
/**
* fu_common_bytes_pad:
* @bytes: a #GBytes
* @sz: the desired size in bytes
*
* Pads a GBytes to a given @sz with `0xff`.
*
* Return value: (transfer full): a #GBytes
**/
GBytes *
fu_common_bytes_pad (GBytes *bytes, gsize sz)
{
gsize bytes_sz;
g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
/* pad */
bytes_sz = g_bytes_get_size (bytes);
if (bytes_sz < sz) {
const guint8 *data = g_bytes_get_data (bytes, NULL);
guint8 *data_new = g_malloc (sz);
memcpy (data_new, data, bytes_sz);
memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
return g_bytes_new_take (data_new, sz);
}
/* exactly right */
return g_bytes_ref (bytes);
}
/** /**
* fu_common_realpath: * fu_common_realpath:
* @filename: a filename * @filename: a filename
@ -1359,3 +1389,27 @@ fu_common_realpath (const gchar *filename, GError **error)
} }
return g_strdup (full_tmp); return g_strdup (full_tmp);
} }
/**
* fu_common_strnsplit:
* @str: a string to split
* @sz: size of @str
* @delimiter: a string which specifies the places at which to split the string
* @max_tokens: the maximum number of pieces to split @str into
*
* Splits a string into a maximum of @max_tokens pieces, using the given
* delimiter. If @max_tokens is reached, the remainder of string is appended
* to the last token.
*
* Return value: a newly-allocated NULL-terminated array of strings
**/
gchar **
fu_common_strnsplit (const gchar *str, gsize sz,
const gchar *delimiter, gint max_tokens)
{
if (str[sz - 1] != '\0') {
g_autofree gchar *str2 = g_strndup (str, sz);
return g_strsplit (str2, delimiter, max_tokens);
}
return g_strsplit (str, delimiter, max_tokens);
}

View File

@ -133,6 +133,8 @@ gboolean fu_common_bytes_is_empty (GBytes *bytes);
gboolean fu_common_bytes_compare (GBytes *bytes1, gboolean fu_common_bytes_compare (GBytes *bytes1,
GBytes *bytes2, GBytes *bytes2,
GError **error); GError **error);
GBytes *fu_common_bytes_pad (GBytes *bytes,
gsize sz);
typedef guint FuEndianType; typedef guint FuEndianType;
@ -150,5 +152,9 @@ guint32 fu_common_read_uint32 (const guint8 *buf,
guint fu_common_string_replace (GString *string, guint fu_common_string_replace (GString *string,
const gchar *search, const gchar *search,
const gchar *replace); const gchar *replace);
gchar **fu_common_strnsplit (const gchar *str,
gsize sz,
const gchar *delimiter,
gint max_tokens);
G_END_DECLS G_END_DECLS

View File

@ -1754,7 +1754,7 @@ fu_device_write_firmware (FuDevice *self,
GError **error) GError **error)
{ {
FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) fw_new = NULL; g_autoptr(FuFirmware) firmware = NULL;
g_return_val_if_fail (FU_IS_DEVICE (self), FALSE); g_return_val_if_fail (FU_IS_DEVICE (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@ -1769,12 +1769,12 @@ fu_device_write_firmware (FuDevice *self,
} }
/* prepare (e.g. decompress) firmware */ /* prepare (e.g. decompress) firmware */
fw_new = fu_device_prepare_firmware (self, fw, flags, error); firmware = fu_device_prepare_firmware (self, fw, flags, error);
if (fw_new == NULL) if (firmware == NULL)
return FALSE; return FALSE;
/* call vfunc */ /* call vfunc */
return klass->write_firmware (self, fw_new, flags, error); return klass->write_firmware (self, firmware, flags, error);
} }
/** /**
@ -1796,7 +1796,7 @@ fu_device_write_firmware (FuDevice *self,
* *
* Since: 1.1.2 * Since: 1.1.2
**/ **/
GBytes * FuFirmware *
fu_device_prepare_firmware (FuDevice *self, fu_device_prepare_firmware (FuDevice *self,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
@ -1804,8 +1804,8 @@ fu_device_prepare_firmware (FuDevice *self,
{ {
FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self); FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self);
FuDevicePrivate *priv = GET_PRIVATE (self); FuDevicePrivate *priv = GET_PRIVATE (self);
guint64 fw_sz; g_autoptr(FuFirmware) firmware = NULL;
g_autoptr(GBytes) fw_new = NULL; g_autoptr(GBytes) fw_def = NULL;
g_return_val_if_fail (FU_IS_DEVICE (self), NULL); g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
g_return_val_if_fail (fw != NULL, NULL); g_return_val_if_fail (fw != NULL, NULL);
@ -1813,37 +1813,41 @@ fu_device_prepare_firmware (FuDevice *self,
/* optionally subclassed */ /* optionally subclassed */
if (klass->prepare_firmware != NULL) { if (klass->prepare_firmware != NULL) {
fw_new = klass->prepare_firmware (self, fw, flags, error); firmware = klass->prepare_firmware (self, fw, flags, error);
if (fw_new == NULL) if (firmware == NULL)
return NULL; return NULL;
} else { } else {
fw_new = g_bytes_ref (fw); firmware = fu_firmware_new_from_bytes (fw);
} }
/* check size */ /* check size */
fw_sz = (guint64) g_bytes_get_size (fw_new); fw_def = fu_firmware_get_image_default_bytes (firmware, NULL);
if (priv->size_max > 0 && fw_sz > priv->size_max) { if (fw_def != NULL) {
g_set_error (error, guint64 fw_sz = (guint64) g_bytes_get_size (fw_def);
FWUPD_ERROR, if (priv->size_max > 0 && fw_sz > priv->size_max) {
FWUPD_ERROR_INVALID_FILE, g_set_error (error,
"firmware is %04x bytes larger than the allowed " FWUPD_ERROR,
"maximum size of %04x bytes", FWUPD_ERROR_INVALID_FILE,
(guint) (fw_sz - priv->size_max), "firmware is %04x bytes larger than the allowed "
(guint) priv->size_max); "maximum size of %04x bytes",
return NULL; (guint) (fw_sz - priv->size_max),
} (guint) priv->size_max);
if (priv->size_min > 0 && fw_sz < priv->size_min) { return NULL;
g_set_error (error, }
FWUPD_ERROR, if (priv->size_min > 0 && fw_sz < priv->size_min) {
FWUPD_ERROR_INVALID_FILE, g_set_error (error,
"firmware is %04x bytes smaller than the allowed " FWUPD_ERROR,
"minimum size of %04x bytes", FWUPD_ERROR_INVALID_FILE,
(guint) (priv->size_min - fw_sz), "firmware is %04x bytes smaller than the allowed "
(guint) priv->size_max); "minimum size of %04x bytes",
return NULL; (guint) (priv->size_min - fw_sz),
(guint) priv->size_max);
return NULL;
}
} }
return g_steal_pointer (&fw_new); /* success */
return g_steal_pointer (&firmware);
} }
/** /**

View File

@ -9,6 +9,7 @@
#include <glib-object.h> #include <glib-object.h>
#include <fwupd.h> #include <fwupd.h>
#include "fu-firmware.h"
#include "fu-quirks.h" #include "fu-quirks.h"
#include "fu-common-version.h" #include "fu-common-version.h"
@ -23,7 +24,7 @@ struct _FuDeviceClass
void (*to_string) (FuDevice *self, void (*to_string) (FuDevice *self,
GString *str); GString *str);
gboolean (*write_firmware) (FuDevice *self, gboolean (*write_firmware) (FuDevice *self,
GBytes *fw, FuFirmware *firmware,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error); GError **error);
GBytes *(*read_firmware) (FuDevice *self, GBytes *(*read_firmware) (FuDevice *self,
@ -38,7 +39,7 @@ struct _FuDeviceClass
GError **error); GError **error);
gboolean (*probe) (FuDevice *self, gboolean (*probe) (FuDevice *self,
GError **error); GError **error);
GBytes *(*prepare_firmware) (FuDevice *self, FuFirmware *(*prepare_firmware) (FuDevice *self,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error); GError **error);
@ -210,7 +211,7 @@ gboolean fu_device_write_firmware (FuDevice *self,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error); GError **error);
GBytes *fu_device_prepare_firmware (FuDevice *self, FuFirmware *fu_device_prepare_firmware (FuDevice *self,
GBytes *fw, GBytes *fw,
FwupdInstallFlags flags, FwupdInstallFlags flags,
GError **error); GError **error);

113
src/fu-firmware-common.c Normal file
View File

@ -0,0 +1,113 @@
/*
* Copyright (C) 2015-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuFirmware"
#include <config.h>
#include <string.h>
#include "fu-firmware-common.h"
/**
* fu_firmware_strparse_uint4:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 1 byte long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint8
fu_firmware_strparse_uint4 (const gchar *data)
{
gchar buffer[2];
memcpy (buffer, data, 1);
buffer[1] = '\0';
return (guint8) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* fu_firmware_strparse_uint8:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 2 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint8
fu_firmware_strparse_uint8 (const gchar *data)
{
gchar buffer[3];
memcpy (buffer, data, 2);
buffer[2] = '\0';
return (guint8) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* fu_firmware_strparse_uint16:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 4 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint16
fu_firmware_strparse_uint16 (const gchar *data)
{
gchar buffer[5];
memcpy (buffer, data, 4);
buffer[4] = '\0';
return (guint16) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* fu_firmware_strparse_uint24:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 6 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint32
fu_firmware_strparse_uint24 (const gchar *data)
{
gchar buffer[7];
memcpy (buffer, data, 6);
buffer[6] = '\0';
return (guint32) g_ascii_strtoull (buffer, NULL, 16);
}
/**
* fu_firmware_strparse_uint32:
* @data: a string
*
* Parses a base 16 number from a string.
*
* The string MUST be at least 8 bytes long as this function cannot check the
* length of @data. Checking the size must be done in the caller.
*
* Return value: A parsed value, or 0 for error
**/
guint32
fu_firmware_strparse_uint32 (const gchar *data)
{
gchar buffer[9];
memcpy (buffer, data, 8);
buffer[8] = '\0';
return (guint32) g_ascii_strtoull (buffer, NULL, 16);
}

19
src/fu-firmware-common.h Normal file
View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2015-2017 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
guint8 fu_firmware_strparse_uint4 (const gchar *data);
guint8 fu_firmware_strparse_uint8 (const gchar *data);
guint16 fu_firmware_strparse_uint16 (const gchar *data);
guint32 fu_firmware_strparse_uint24 (const gchar *data);
guint32 fu_firmware_strparse_uint32 (const gchar *data);
G_END_DECLS

329
src/fu-firmware-image.c Normal file
View File

@ -0,0 +1,329 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuFirmware"
#include "config.h"
#include <string.h>
#include "fu-firmware-image.h"
/**
* SECTION:fu-firmware_image
* @short_description: a firmware_image file
*
* An object that represents a firmware_image file.
*/
typedef struct {
gchar *id;
GBytes *bytes;
guint64 addr;
guint64 idx;
} FuFirmwareImagePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (FuFirmwareImage, fu_firmware_image, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fu_firmware_image_get_instance_private (o))
/**
* fu_firmware_image_set_id:
* @self: a #FuPlugin
* @id: image ID, e.g. "config"
*
* Since: 1.2.11
**/
void
fu_firmware_image_set_id (FuFirmwareImage *self, const gchar *id)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self));
g_return_if_fail (id != NULL);
g_free (priv->id);
priv->id = g_strdup (id);
}
/**
* fu_firmware_image_get_id:
* @self: a #FuPlugin
*
* Gets the image ID, typically set at construction.
*
* Returns: image ID, e.g. "config"
*
* Since: 1.2.11
**/
const gchar *
fu_firmware_image_get_id (FuFirmwareImage *self)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE);
return priv->id;
}
/**
* fu_firmware_image_set_addr:
* @self: a #FuPlugin
* @addr: integer
*
* Sets the base address of the image.
*
* Since: 1.2.11
**/
void
fu_firmware_image_set_addr (FuFirmwareImage *self, guint64 addr)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self));
priv->addr = addr;
}
/**
* fu_firmware_image_get_addr:
* @self: a #FuPlugin
*
* Gets the base address of the image.
*
* Returns: integer
*
* Since: 1.2.11
**/
guint64
fu_firmware_image_get_addr (FuFirmwareImage *self)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64);
return priv->addr;
}
/**
* fu_firmware_image_set_idx:
* @self: a #FuPlugin
* @idx: integer
*
* Sets the index of the image which is used for ordering.
*
* Since: 1.2.11
**/
void
fu_firmware_image_set_idx (FuFirmwareImage *self, guint64 idx)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self));
priv->idx = idx;
}
/**
* fu_firmware_image_get_idx:
* @self: a #FuPlugin
*
* Gets the index of the image which is used for ordering.
*
* Returns: integer
*
* Since: 1.2.11
**/
guint64
fu_firmware_image_get_idx (FuFirmwareImage *self)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), G_MAXUINT64);
return priv->idx;
}
/**
* fu_firmware_image_set_bytes:
* @self: a #FuPlugin
* @bytes: A #GBytes
*
* Sets the contents of the image if not created with fu_firmware_image_new().
*
* Since: 1.2.11
**/
void
fu_firmware_image_set_bytes (FuFirmwareImage *self, GBytes *bytes)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FU_IS_FIRMWARE_IMAGE (self));
g_return_if_fail (bytes != NULL);
g_return_if_fail (priv->bytes == NULL);
priv->bytes = g_bytes_ref (bytes);
}
/**
* fu_firmware_image_get_bytes:
* @self: a #FuPlugin
* @error: A #GError, or %NULL
*
* Gets the contents of the bytes.
*
* Returns: (transfer full): a #GBytes of the bytes, or %NULL if the bytes is not set
*
* Since: 1.2.11
**/
GBytes *
fu_firmware_image_get_bytes (FuFirmwareImage *self, GError **error)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_FIRMWARE_IMAGE (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (priv->bytes == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"no bytes found in firmware bytes %s", priv->id);
return NULL;
}
return g_bytes_ref (priv->bytes);
}
/**
* fu_firmware_image_get_bytes_chunk:
* @self: a #FuFirmwareImage
* @address: an address greater than dfu_element_get_address()
* @chunk_sz_max: the size of the new chunk
* @error: a #GError, or %NULL
*
* Gets a block of data from the image. If the contents of the image is
* smaller than the requested chunk size then the #GBytes will be smaller
* than @chunk_sz_max. Use fu_common_bytes_pad() if padding is required.
*
* If the @address is larger than the size of the image then an error is returned.
*
* Return value: (transfer full): a #GBytes, or %NULL
*
* Since: 1.2.11
**/
GBytes *
fu_firmware_image_get_bytes_chunk (FuFirmwareImage *self,
guint64 address,
guint64 chunk_sz_max,
GError **error)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
gsize chunk_left;
guint64 offset;
/* check address requested is larger than base address */
if (address < priv->addr) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"requested address 0x%x less than base address 0x%x",
(guint) address, (guint) priv->addr);
return NULL;
}
/* offset into data */
offset = address - priv->addr;
if (offset > g_bytes_get_size (priv->bytes)) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"offset 0x%x larger than data size 0x%x",
(guint) offset,
(guint) g_bytes_get_size (priv->bytes));
return NULL;
}
/* if we have less data than requested */
chunk_left = g_bytes_get_size (priv->bytes) - offset;
if (chunk_sz_max > chunk_left)
return g_bytes_new_from_bytes (priv->bytes, offset, chunk_left);
/* check chunk */
return g_bytes_new_from_bytes (priv->bytes, offset, chunk_sz_max);
}
static void
fwupd_pad_kv_str (GString *str, const gchar *key, const gchar *value)
{
/* ignore */
if (key == NULL || value == NULL)
return;
g_string_append_printf (str, " %s: ", key);
for (gsize i = strlen (key); i < 20; i++)
g_string_append (str, " ");
g_string_append_printf (str, "%s\n", value);
}
/**
* fu_firmware_image_to_string:
* @self: A #FuFirmwareImage
*
* This allows us to easily print the object.
*
* Returns: a string value, or %NULL for invalid.
*
* Since: 1.2.11
**/
gchar *
fu_firmware_image_to_string (FuFirmwareImage *self)
{
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
GString *str = g_string_new (" FuFirmwareImage:\n");
if (priv->id != NULL)
fwupd_pad_kv_str (str, "ID", priv->id);
if (priv->idx != 0x0) {
g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->idx);
fwupd_pad_kv_str (str, "Index", tmp);
}
if (priv->addr != 0x0) {
g_autofree gchar *tmp = g_strdup_printf ("0x%04x", (guint) priv->addr);
fwupd_pad_kv_str (str, "Address", tmp);
}
if (priv->bytes != NULL) {
gsize sz = g_bytes_get_size (priv->bytes);
g_autofree gchar *tmp = g_strdup_printf ("%04x", (guint) sz);
fwupd_pad_kv_str (str, "Data", tmp);
}
return g_string_free (str, FALSE);
}
static void
fu_firmware_image_init (FuFirmwareImage *self)
{
}
static void
fu_firmware_image_finalize (GObject *object)
{
FuFirmwareImage *self = FU_FIRMWARE_IMAGE (object);
FuFirmwareImagePrivate *priv = GET_PRIVATE (self);
g_free (priv->id);
if (priv->bytes != NULL)
g_bytes_unref (priv->bytes);
G_OBJECT_CLASS (fu_firmware_image_parent_class)->finalize (object);
}
static void
fu_firmware_image_class_init (FuFirmwareImageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_firmware_image_finalize;
}
/**
* fu_firmware_image_new:
* @id: Optional ID
* @bytes: Optional #GBytes
*
* Creates an empty firmware_image object.
*
* Returns: a #FuFirmwareImage
*
* Since: 1.2.11
**/
FuFirmwareImage *
fu_firmware_image_new (GBytes *bytes)
{
FuFirmwareImage *self = g_object_new (FU_TYPE_FIRMWARE_IMAGE, NULL);
if (bytes != NULL)
fu_firmware_image_set_bytes (self, bytes);
return self;
}

49
src/fu-firmware-image.h Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <glib-object.h>
#include <fwupd.h>
G_BEGIN_DECLS
#define FU_TYPE_FIRMWARE_IMAGE (fu_firmware_image_get_type ())
G_DECLARE_DERIVABLE_TYPE (FuFirmwareImage, fu_firmware_image, FU, FIRMWARE_IMAGE, GObject)
struct _FuFirmwareImageClass
{
GObjectClass parent_class;
gboolean (*parse) (FuFirmwareImage *self,
GBytes *fw,
FwupdInstallFlags flags,
GError **error);
/*< private >*/
gpointer padding[30];
};
FuFirmwareImage *fu_firmware_image_new (GBytes *bytes);
gchar *fu_firmware_image_to_string (FuFirmwareImage *self);
const gchar *fu_firmware_image_get_id (FuFirmwareImage *self);
void fu_firmware_image_set_id (FuFirmwareImage *self,
const gchar *id);
guint64 fu_firmware_image_get_addr (FuFirmwareImage *self);
void fu_firmware_image_set_addr (FuFirmwareImage *self,
guint64 addr);
guint64 fu_firmware_image_get_idx (FuFirmwareImage *self);
void fu_firmware_image_set_idx (FuFirmwareImage *self,
guint64 idx);
void fu_firmware_image_set_bytes (FuFirmwareImage *self,
GBytes *bytes);
GBytes *fu_firmware_image_get_bytes (FuFirmwareImage *self,
GError **error);
GBytes *fu_firmware_image_get_bytes_chunk(FuFirmwareImage *self,
guint64 address,
guint64 chunk_sz_max,
GError **error);
G_END_DECLS

402
src/fu-firmware.c Normal file
View File

@ -0,0 +1,402 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuFirmware"
#include "config.h"
#include "fu-firmware.h"
/**
* SECTION:fu-firmware
* @short_description: a firmware file
*
* An object that represents a firmware file.
*/
typedef struct {
GPtrArray *images; /* FuFirmwareImage */
} FuFirmwarePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (FuFirmware, fu_firmware, G_TYPE_OBJECT)
#define GET_PRIVATE(o) (fu_firmware_get_instance_private (o))
/**
* fu_firmware_parse_full:
* @self: A #FuFirmware
* @image: A #GBytes
* @addr_start: Start address, useful for ignoring a bootloader
* @addr_end: End address, useful for ignoring config bytes
* @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
* @error: A #GError, or %NULL
*
* Parses a firmware, typically breaking the firmware into images.
*
* Returns: %TRUE for success
*
* Since: 1.2.11
**/
gboolean
fu_firmware_parse_full (FuFirmware *self,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self);
g_autoptr(FuFirmwareImage) img = NULL;
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
g_return_val_if_fail (fw != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* subclassed */
if (klass->parse != NULL)
return klass->parse (self, fw, addr_start, addr_end, flags, error);
/* just add entire blob */
img = fu_firmware_image_new (fw);
fu_firmware_add_image (self, img);
return TRUE;
}
/**
* fu_firmware_parse:
* @self: A #FuFirmware
* @image: A #GBytes
* @flags: some #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
* @error: A #GError, or %NULL
*
* Parses a firmware, typically breaking the firmware into images.
*
* Returns: %TRUE for success
*
* Since: 1.2.11
**/
gboolean
fu_firmware_parse (FuFirmware *self, GBytes *fw, FwupdInstallFlags flags, GError **error)
{
return fu_firmware_parse_full (self, fw, 0x0, 0x0, flags, error);
}
/**
* fu_firmware_write:
* @self: A #FuFirmware
* @error: A #GError, or %NULL
*
* Writes a firmware, typically packing the images into a binary blob.
*
* Returns: (transfer full): a #GBytes
*
* Since: 1.2.11
**/
GBytes *
fu_firmware_write (FuFirmware *self, GError **error)
{
FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS (self);
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* subclassed */
if (klass->write != NULL)
return klass->write (self, error);
/* just add default blob */
return fu_firmware_get_image_default_bytes (self, error);
}
/**
* fu_firmware_add_image:
* @self: a #FuPlugin
* @img: A #FuFirmwareImage
*
* Adds an image to the firmware.
*
* If an image with the same ID is already present it is replaced.
*
* Since: 1.2.11
**/
void
fu_firmware_add_image (FuFirmware *self, FuFirmwareImage *img)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
g_return_if_fail (FU_IS_FIRMWARE (self));
g_return_if_fail (FU_IS_FIRMWARE_IMAGE (img));
g_ptr_array_add (priv->images, g_object_ref (img));
}
/**
* fu_firmware_get_images:
* @self: a #FuPlugin
*
* Returns all the images in the firmware.
*
* Returns: (transfer container) (element-type FuFirmwareImage): images
*
* Since: 1.2.11
**/
GPtrArray *
fu_firmware_get_images (FuFirmware *self)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
g_autoptr(GPtrArray) imgs = NULL;
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
imgs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
for (guint i = 0; i < priv->images->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
g_ptr_array_add (imgs, g_object_ref (img));
}
return g_steal_pointer (&imgs);
}
/**
* fu_firmware_get_image_by_id:
* @self: a #FuPlugin
* @id: (nullable): image ID, e.g. "config"
* @error: A #GError, or %NULL
*
* Gets the firmware image using the image ID.
*
* Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found
*
* Since: 1.2.11
**/
FuFirmwareImage *
fu_firmware_get_image_by_id (FuFirmware *self, const gchar *id, GError **error)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_FIRMWARE (self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
for (guint i = 0; i < priv->images->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
if (g_strcmp0 (fu_firmware_image_get_id (img), id) == 0)
return g_object_ref (img);
}
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"no image id %s found in firmware", id);
return NULL;
}
/**
* fu_firmware_get_image_by_id_bytes:
* @self: a #FuPlugin
* @id: (nullable): image ID, e.g. "config"
* @error: A #GError, or %NULL
*
* Gets the firmware image bytes using the image ID.
*
* Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found
*
* Since: 1.2.11
**/
GBytes *
fu_firmware_get_image_by_id_bytes (FuFirmware *self, const gchar *id, GError **error)
{
g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_by_id (self, id, error);
if (img == NULL)
return NULL;
return fu_firmware_image_get_bytes (img, error);
}
/**
* fu_firmware_get_image_by_idx:
* @self: a #FuPlugin
* @idx: image index
* @error: A #GError, or %NULL
*
* Gets the firmware image using the image index.
*
* Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found
*
* Since: 1.2.11
**/
FuFirmwareImage *
fu_firmware_get_image_by_idx (FuFirmware *self, guint64 idx, GError **error)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_FIRMWARE (self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
for (guint i = 0; i < priv->images->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
if (fu_firmware_image_get_idx (img) == idx)
return g_object_ref (img);
}
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"no image idx %" G_GUINT64_FORMAT " found in firmware", idx);
return NULL;
}
/**
* fu_firmware_get_image_by_idx_bytes:
* @self: a #FuPlugin
* @idx: image index
* @error: A #GError, or %NULL
*
* Gets the firmware image bytes using the image index.
*
* Returns: (transfer full): a #GBytes of a #FuFirmwareImage, or %NULL if the image is not found
*
* Since: 1.2.11
**/
GBytes *
fu_firmware_get_image_by_idx_bytes (FuFirmware *self, guint64 idx, GError **error)
{
g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_by_idx (self, idx, error);
if (img == NULL)
return NULL;
return fu_firmware_image_get_bytes (img, error);
}
/**
* fu_firmware_get_image_default:
* @self: a #FuPlugin
* @error: A #GError, or %NULL
*
* Gets the default firmware image.
*
* NOTE: If the firmware has multiple images included then fu_firmware_get_image_by_id()
* or fu_firmware_get_image_by_idx() must be used rather than this function.
*
* Returns: (transfer full): a #FuFirmwareImage, or %NULL if the image is not found
*
* Since: 1.2.11
**/
FuFirmwareImage *
fu_firmware_get_image_default (FuFirmware *self, GError **error)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
if (priv->images->len == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"no images in firmware");
return NULL;
}
if (priv->images->len > 1) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_FOUND,
"multiple images present in firmware");
return NULL;
}
return g_object_ref (FU_FIRMWARE_IMAGE (g_ptr_array_index (priv->images, 0)));
}
/**
* fu_firmware_get_image_default_bytes:
* @self: a #FuPlugin
* @error: A #GError, or %NULL
*
* Gets the default firmware image.
*
* Returns: (transfer full): a #GBytes of the image, or %NULL if the image is not found
*
* Since: 1.2.11
**/
GBytes *
fu_firmware_get_image_default_bytes (FuFirmware *self, GError **error)
{
g_autoptr(FuFirmwareImage) img = fu_firmware_get_image_default (self, error);
if (img == NULL)
return NULL;
return fu_firmware_image_get_bytes (img, error);
}
/**
* fu_firmware_to_string:
* @self: A #FuFirmware
*
* This allows us to easily print the object.
*
* Returns: a string value, or %NULL for invalid.
*
* Since: 1.2.11
**/
gchar *
fu_firmware_to_string (FuFirmware *self)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
GString *str = g_string_new ("FuFirmware:\n");
for (guint i = 0; i < priv->images->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (priv->images, i);
g_autofree gchar *tmp = fu_firmware_image_to_string (img);
g_string_append (str, tmp);
}
return g_string_free (str, FALSE);
}
static void
fu_firmware_init (FuFirmware *self)
{
FuFirmwarePrivate *priv = GET_PRIVATE (self);
priv->images = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
}
static void
fu_firmware_finalize (GObject *object)
{
FuFirmware *self = FU_FIRMWARE (object);
FuFirmwarePrivate *priv = GET_PRIVATE (self);
g_ptr_array_unref (priv->images);
G_OBJECT_CLASS (fu_firmware_parent_class)->finalize (object);
}
static void
fu_firmware_class_init (FuFirmwareClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_firmware_finalize;
}
/**
* fu_firmware_new:
*
* Creates an empty firmware object.
*
* Returns: a #FuFirmware
*
* Since: 1.2.11
**/
FuFirmware *
fu_firmware_new (void)
{
FuFirmware *self = g_object_new (FU_TYPE_FIRMWARE, NULL);
return FU_FIRMWARE (self);
}
/**
* fu_firmware_new_from_bytes:
* @self: A #GBytes image
*
* Creates a firmware object with the provided image set as default.
*
* Returns: a #FuFirmware
*
* Since: 1.2.11
**/
FuFirmware *
fu_firmware_new_from_bytes (GBytes *fw)
{
FuFirmware *self = fu_firmware_new ();
g_autoptr(FuFirmwareImage) img = NULL;
img = fu_firmware_image_new (fw);
fu_firmware_add_image (self, img);
return self;
}

71
src/fu-firmware.h Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <glib-object.h>
#include <fwupd.h>
#include "fu-firmware-image.h"
G_BEGIN_DECLS
#define FU_TYPE_FIRMWARE (fu_firmware_get_type ())
G_DECLARE_DERIVABLE_TYPE (FuFirmware, fu_firmware, FU, FIRMWARE, GObject)
struct _FuFirmwareClass
{
GObjectClass parent_class;
gboolean (*parse) (FuFirmware *self,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error);
GBytes *(*write) (FuFirmware *self,
GError **error);
/*< private >*/
gpointer padding[30];
};
FuFirmware *fu_firmware_new (void);
FuFirmware *fu_firmware_new_from_bytes (GBytes *fw);
gchar *fu_firmware_to_string (FuFirmware *self);
gboolean fu_firmware_parse (FuFirmware *self,
GBytes *fw,
FwupdInstallFlags flags,
GError **error);
gboolean fu_firmware_parse_full (FuFirmware *self,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error);
GBytes *fu_firmware_write (FuFirmware *self,
GError **error);
void fu_firmware_add_image (FuFirmware *self,
FuFirmwareImage *image);
GPtrArray *fu_firmware_get_images (FuFirmware *self);
FuFirmwareImage *fu_firmware_get_image_by_id (FuFirmware *self,
const gchar *id,
GError **error);
GBytes *fu_firmware_get_image_by_id_bytes (FuFirmware *self,
const gchar *id,
GError **error);
FuFirmwareImage *fu_firmware_get_image_by_idx (FuFirmware *self,
guint64 idx,
GError **error);
GBytes *fu_firmware_get_image_by_idx_bytes (FuFirmware *self,
guint64 idx,
GError **error);
FuFirmwareImage *fu_firmware_get_image_default (FuFirmware *self,
GError **error);
GBytes *fu_firmware_get_image_default_bytes (FuFirmware *self,
GError **error);
G_END_DECLS

377
src/fu-ihex-firmware.c Normal file
View File

@ -0,0 +1,377 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuFirmware"
#include "config.h"
#include <string.h>
#include "fu-common.h"
#include "fu-firmware-common.h"
#include "fu-ihex-firmware.h"
struct _FuIhexFirmware {
FuFirmware parent_instance;
};
G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE)
#define DFU_INHX32_RECORD_TYPE_DATA 0x00
#define DFU_INHX32_RECORD_TYPE_EOF 0x01
#define DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT 0x02
#define DFU_INHX32_RECORD_TYPE_START_SEGMENT 0x03
#define DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR 0x04
#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05
#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd
static const gchar *
fu_ihex_firmware_record_type_to_string (guint8 record_type)
{
if (record_type == DFU_INHX32_RECORD_TYPE_DATA)
return "DATA";
if (record_type == DFU_INHX32_RECORD_TYPE_EOF)
return "EOF";
if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT)
return "EXTENDED_SEGMENT";
if (record_type == DFU_INHX32_RECORD_TYPE_START_SEGMENT)
return "START_SEGMENT";
if (record_type == DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR)
return "EXTENDED_LINEAR";
if (record_type == DFU_INHX32_RECORD_TYPE_START_LINEAR)
return "ADDR32";
if (record_type == DFU_INHX32_RECORD_TYPE_SIGNATURE)
return "SIGNATURE";
return NULL;
}
static gboolean
fu_ihex_firmware_parse (FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
const gchar *data;
gboolean got_eof = FALSE;
gsize sz = 0;
guint32 abs_addr = 0x0;
guint32 addr_last = 0x0;
guint32 img_addr = G_MAXUINT32;
guint32 seg_addr = 0x0;
g_auto(GStrv) lines = NULL;
g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL);
g_autoptr(GBytes) img_bytes = NULL;
g_autoptr(GString) buf = g_string_new (NULL);
g_autoptr(GByteArray) buf_signature = g_byte_array_new ();
g_return_val_if_fail (fw != NULL, FALSE);
/* parse records */
data = g_bytes_get_data (fw, &sz);
lines = fu_common_strnsplit (data, sz, "\n", -1);
for (guint ln = 0; lines[ln] != NULL; ln++) {
const gchar *line = lines[ln];
gsize linesz;
guint32 addr;
guint8 byte_cnt;
guint8 record_type;
guint line_end;
/* ignore comments */
if (g_str_has_prefix (line, ";"))
continue;
/* ignore blank lines */
g_strdelimit (lines[ln], "\r\x1a", '\0');
linesz = strlen (line);
if (linesz == 0)
continue;
/* check starting token */
if (line[0] != ':') {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid starting token on line %u: %s",
ln + 1, line);
return FALSE;
}
/* check there's enough data for the smallest possible record */
if (linesz < 11) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"line %u is incomplete, length %u",
ln + 1, (guint) linesz);
return FALSE;
}
/* length, 16-bit address, type */
byte_cnt = fu_firmware_strparse_uint8 (line + 1);
addr = fu_firmware_strparse_uint16 (line + 3);
record_type = fu_firmware_strparse_uint8 (line + 7);
g_debug ("%s:", fu_ihex_firmware_record_type_to_string (record_type));
g_debug (" addr_start:\t0x%04x", addr);
g_debug (" length:\t0x%02x", byte_cnt);
addr += seg_addr;
addr += abs_addr;
g_debug (" addr:\t0x%08x", addr);
/* position of checksum */
line_end = 9 + byte_cnt * 2;
if (line_end > (guint) linesz) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"line %u malformed, length: %u",
ln + 1, line_end);
return FALSE;
}
/* verify checksum */
if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
guint8 checksum = 0;
for (guint i = 1; i < line_end + 2; i += 2) {
guint8 data_tmp = fu_firmware_strparse_uint8 (line + i);
checksum += data_tmp;
}
if (checksum != 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"line %u has invalid checksum (0x%02x)",
ln + 1, checksum);
return FALSE;
}
}
/* process different record types */
switch (record_type) {
case DFU_INHX32_RECORD_TYPE_DATA:
/* base address for element */
if (img_addr == G_MAXUINT32)
img_addr = addr;
/* does not make sense */
if (addr < addr_last) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid address 0x%x, last was 0x%x",
(guint) addr,
(guint) addr_last);
return FALSE;
}
/* parse bytes from line */
g_debug ("writing data 0x%08x", (guint32) addr);
for (guint i = 9; i < line_end; i += 2) {
/* any holes in the hex record */
guint32 len_hole = addr - addr_last;
guint8 data_tmp;
if (addr_last > 0 && len_hole > 0x100000) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"hole of 0x%x bytes too large to fill",
(guint) len_hole);
return FALSE;
}
if (addr_last > 0x0 && len_hole > 1) {
g_debug ("filling address 0x%08x to 0x%08x",
addr_last + 1, addr_last + len_hole - 1);
for (guint j = 1; j < len_hole; j++) {
/* although 0xff might be clearer,
* we can't write 0xffff to pic14 */
g_string_append_c (buf, 0x00);
}
}
/* write into buf */
data_tmp = fu_firmware_strparse_uint8 (line + i);
g_string_append_c (buf, (gchar) data_tmp);
addr_last = addr++;
}
break;
case DFU_INHX32_RECORD_TYPE_EOF:
if (got_eof) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"duplicate EOF, perhaps "
"corrupt file");
return FALSE;
}
got_eof = TRUE;
break;
case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR:
abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16;
g_debug (" abs_addr:\t0x%02x", abs_addr);
break;
case DFU_INHX32_RECORD_TYPE_START_LINEAR:
abs_addr = fu_firmware_strparse_uint32 (line + 9);
g_debug (" abs_addr:\t0x%08x", abs_addr);
break;
case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT:
/* segment base address, so ~1Mb addressable */
seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16;
g_debug (" seg_addr:\t0x%08x", seg_addr);
break;
case DFU_INHX32_RECORD_TYPE_START_SEGMENT:
/* initial content of the CS:IP registers */
seg_addr = fu_firmware_strparse_uint32 (line + 9);
g_debug (" seg_addr:\t0x%02x", seg_addr);
break;
case DFU_INHX32_RECORD_TYPE_SIGNATURE:
for (guint i = 9; i < line_end; i += 2) {
guint8 tmp_c = fu_firmware_strparse_uint8 (line + i);
g_byte_array_append (buf_signature, &tmp_c, 1);
}
break;
default:
/* vendors sneak in nonstandard sections past the EOF */
if (got_eof)
break;
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid ihex record type %i",
record_type);
return FALSE;
}
}
/* no EOF */
if (!got_eof) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no EOF, perhaps truncated file");
return FALSE;
}
/* add single image */
img_bytes = g_bytes_new (buf->str, buf->len);
fu_firmware_image_set_bytes (img, img_bytes);
if (img_addr != G_MAXUINT32)
fu_firmware_image_set_addr (img, img_addr);
fu_firmware_add_image (firmware, img);
/* add optional signature */
if (buf_signature->len > 0) {
g_autoptr(GBytes) data_sig = g_bytes_new (buf_signature->data, buf_signature->len);
g_autoptr(FuFirmwareImage) img_sig = fu_firmware_image_new (data_sig);
fu_firmware_image_set_id (img_sig, "signature");
fu_firmware_add_image (firmware, img_sig);
}
return TRUE;
}
static void
fu_ihex_firmware_emit_chunk (GString *str,
guint16 address,
guint8 record_type,
const guint8 *data,
gsize sz)
{
guint8 checksum = 0x00;
g_string_append_printf (str, ":%02X%04X%02X",
(guint) sz,
(guint) address,
(guint) record_type);
for (gsize j = 0; j < sz; j++)
g_string_append_printf (str, "%02X", data[j]);
checksum = (guint8) sz;
checksum += (guint8) ((address & 0xff00) >> 8);
checksum += (guint8) (address & 0xff);
checksum += record_type;
for (gsize j = 0; j < sz; j++)
checksum += data[j];
g_string_append_printf (str, "%02X\n", (guint) (((~checksum) + 0x01) & 0xff));
}
static gboolean
dfu_firmware_to_ihex_image (FuFirmwareImage *img, GString *str, GError **error)
{
const guint8 *data;
const guint chunk_size = 16;
gsize len;
guint32 address_offset_last = 0x0;
guint8 record_type = DFU_INHX32_RECORD_TYPE_DATA;
g_autoptr(GBytes) bytes = NULL;
/* get data */
bytes = fu_firmware_image_get_bytes (img, error);
if (bytes == NULL)
return FALSE;
/* special case */
if (g_strcmp0 (fu_firmware_image_get_id (img), "signature") == 0)
record_type = DFU_INHX32_RECORD_TYPE_SIGNATURE;
/* get number of chunks */
data = g_bytes_get_data (bytes, &len);
for (gsize i = 0; i < len; i += chunk_size) {
guint32 address_tmp = fu_firmware_image_get_addr (img) + i;
guint32 address_offset = (address_tmp >> 16) & 0xffff;
gsize chunk_len = MIN (len - i, 16);
/* need to offset */
if (address_offset != address_offset_last) {
guint8 buf[2];
fu_common_write_uint16 (buf, address_offset, G_BIG_ENDIAN);
fu_ihex_firmware_emit_chunk (str, 0x0,
DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR,
buf, 2);
address_offset_last = address_offset;
}
address_tmp &= 0xffff;
fu_ihex_firmware_emit_chunk (str, address_tmp,
record_type, data + i, chunk_len);
}
return TRUE;
}
static GBytes *
fu_ihex_firmware_write (FuFirmware *firmware, GError **error)
{
g_autoptr(GPtrArray) imgs = NULL;
g_autoptr(GString) str = NULL;
/* write all the element data */
str = g_string_new ("");
imgs = fu_firmware_get_images (firmware);
for (guint i = 0; i < imgs->len; i++) {
FuFirmwareImage *img = g_ptr_array_index (imgs, i);
if (!dfu_firmware_to_ihex_image (img, str, error))
return NULL;
}
/* add EOF */
fu_ihex_firmware_emit_chunk (str, 0x0, DFU_INHX32_RECORD_TYPE_EOF, NULL, 0);
return g_bytes_new (str->str, str->len);
}
static void
fu_ihex_firmware_init (FuIhexFirmware *self)
{
}
static void
fu_ihex_firmware_class_init (FuIhexFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
klass_firmware->parse = fu_ihex_firmware_parse;
klass_firmware->write = fu_ihex_firmware_write;
}
FuFirmware *
fu_ihex_firmware_new (void)
{
return FU_FIRMWARE (g_object_new (FU_TYPE_IHEX_FIRMWARE, NULL));
}

18
src/fu-ihex-firmware.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include "fu-firmware.h"
G_BEGIN_DECLS
#define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type ())
G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware)
FuFirmware *fu_ihex_firmware_new (void);
G_END_DECLS

View File

@ -23,6 +23,7 @@
#include "fu-device-list.h" #include "fu-device-list.h"
#include "fu-device-private.h" #include "fu-device-private.h"
#include "fu-engine.h" #include "fu-engine.h"
#include "fu-ihex-firmware.h"
#include "fu-quirks.h" #include "fu-quirks.h"
#include "fu-keyring.h" #include "fu-keyring.h"
#include "fu-history.h" #include "fu-history.h"
@ -33,6 +34,7 @@
#include "fu-hash.h" #include "fu-hash.h"
#include "fu-hwids.h" #include "fu-hwids.h"
#include "fu-smbios.h" #include "fu-smbios.h"
#include "fu-srec-firmware.h"
#include "fu-test.h" #include "fu-test.h"
#ifdef ENABLE_GPG #ifdef ENABLE_GPG
@ -3665,6 +3667,248 @@ fu_common_vercmp_func (void)
g_assert_cmpint (fu_common_vercmp (NULL, NULL), ==, G_MAXINT); g_assert_cmpint (fu_common_vercmp (NULL, NULL), ==, G_MAXINT);
} }
static void
fu_firmware_ihex_func (void)
{
const guint8 *data;
gboolean ret;
gsize len;
g_autofree gchar *filename_hex = NULL;
g_autofree gchar *filename_ref = NULL;
g_autofree gchar *str = NULL;
g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new ();
g_autoptr(GBytes) data_file = NULL;
g_autoptr(GBytes) data_fw = NULL;
g_autoptr(GBytes) data_hex = NULL;
g_autoptr(GBytes) data_ref = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_ref = NULL;
g_autoptr(GFile) file_hex = NULL;
/* load a Intel hex32 file */
filename_hex = fu_test_get_filename (TESTDATADIR, "firmware.hex");
g_assert (filename_hex != NULL);
file_hex = g_file_new_for_path (filename_hex);
data_file = g_file_load_bytes (file_hex, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (data_file != NULL);
ret = fu_firmware_parse (firmware, data_file, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
data_fw = fu_firmware_get_image_default_bytes (firmware, &error);
g_assert_no_error (error);
g_assert_nonnull (data_fw);
g_assert_cmpint (g_bytes_get_size (data_fw), ==, 136);
/* did we match the reference file? */
filename_ref = fu_test_get_filename (TESTDATADIR, "firmware.bin");
g_assert (filename_ref != NULL);
file_ref = g_file_new_for_path (filename_ref);
data_ref = g_file_load_bytes (file_ref, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (data_ref != NULL);
ret = fu_common_bytes_compare (data_fw, data_ref, &error);
g_assert_no_error (error);
g_assert_true (ret);
/* export a ihex file (which will be slightly different due to
* non-continous regions being expanded */
data_hex = fu_firmware_write (firmware, &error);
g_assert_no_error (error);
g_assert (data_hex != NULL);
data = g_bytes_get_data (data_hex, &len);
str = g_strndup ((const gchar *) data, len);
g_assert_cmpstr (str, ==,
":104000003DEF20F000000000FACF01F0FBCF02F0FE\n"
":10401000E9CF03F0EACF04F0E1CF05F0E2CF06F0FC\n"
":10402000D9CF07F0DACF08F0F3CF09F0F4CF0AF0D8\n"
":10403000F6CF0BF0F7CF0CF0F8CF0DF0F5CF0EF078\n"
":104040000EC0F5FF0DC0F8FF0CC0F7FF0BC0F6FF68\n"
":104050000AC0F4FF09C0F3FF08C0DAFF07C0D9FFA8\n"
":1040600006C0E2FF05C0E1FF04C0EAFF03C0E9FFAC\n"
":1040700002C0FBFF01C0FAFF11003FEF20F000017A\n"
":0840800042EF20F03DEF20F0BB\n"
":00000001FF\n");
}
static void
fu_firmware_ihex_signed_func (void)
{
const guint8 *data;
gboolean ret;
gsize len;
g_autofree gchar *filename_shex = NULL;
g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new ();
g_autoptr(GBytes) data_file = NULL;
g_autoptr(GBytes) data_fw = NULL;
g_autoptr(GBytes) data_sig = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_hex = NULL;
/* load a signed Intel hex32 file */
filename_shex = fu_test_get_filename (TESTDATADIR, "firmware.shex");
g_assert (filename_shex != NULL);
file_hex = g_file_new_for_path (filename_shex);
data_file = g_file_load_bytes (file_hex, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (data_file != NULL);
ret = fu_firmware_parse (firmware, data_file, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
data_fw = fu_firmware_get_image_by_id_bytes (firmware, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (data_fw);
g_assert_cmpint (g_bytes_get_size (data_fw), ==, 136);
/* get the signed image */
data_sig = fu_firmware_get_image_by_id_bytes (firmware, "signature", &error);
g_assert_no_error (error);
g_assert_nonnull (data_sig);
data = g_bytes_get_data (data_sig, &len);
g_assert_cmpint (len, ==, 8);
g_assert (data != NULL);
g_assert_cmpint (memcmp (data, "deadbeef", 8), ==, 0);
}
static void
fu_firmware_ihex_offset_func (void)
{
const guint8 *data;
gboolean ret;
gsize len;
g_autofree gchar *str = NULL;
g_autoptr(FuFirmware) firmware = fu_ihex_firmware_new ();
g_autoptr(FuFirmware) firmware_verify = fu_ihex_firmware_new ();
g_autoptr(FuFirmwareImage) img_verify = NULL;
g_autoptr(FuFirmwareImage) img = NULL;
g_autoptr(GBytes) data_bin = NULL;
g_autoptr(GBytes) data_dummy = NULL;
g_autoptr(GBytes) data_verify = NULL;
g_autoptr(GError) error = NULL;
/* add a 4 byte image in high memory */
data_dummy = g_bytes_new_static ("foo", 4);
img = fu_firmware_image_new (data_dummy);
fu_firmware_image_set_addr (img, 0x80000000);
fu_firmware_add_image (firmware, img);
data_bin = fu_firmware_write (firmware, &error);
g_assert_no_error (error);
g_assert (data_bin != NULL);
data = g_bytes_get_data (data_bin, &len);
str = g_strndup ((const gchar *) data, len);
g_assert_cmpstr (str, ==,
":0200000480007A\n"
":04000000666F6F00B8\n"
":00000001FF\n");
/* check we can load it too */
ret = fu_firmware_parse (firmware_verify, data_bin, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
img_verify = fu_firmware_get_image_default (firmware_verify, &error);
g_assert_no_error (error);
g_assert (img_verify != NULL);
g_assert_cmpint (fu_firmware_image_get_addr (img_verify), ==, 0x80000000);
data_verify = fu_firmware_image_get_bytes (img_verify, &error);
g_assert_no_error (error);
g_assert (data_verify != NULL);
g_assert_cmpint (g_bytes_get_size (data_verify), ==, 0x4);
}
static void
fu_firmware_srec_func (void)
{
gboolean ret;
g_autofree gchar *filename_srec = NULL;
g_autofree gchar *filename_ref = NULL;
g_autoptr(FuFirmware) firmware = fu_srec_firmware_new ();
g_autoptr(GBytes) data_ref = NULL;
g_autoptr(GBytes) data_srec = NULL;
g_autoptr(GBytes) data_bin = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_bin = NULL;
g_autoptr(GFile) file_srec = NULL;
filename_srec = fu_test_get_filename (TESTDATADIR, "firmware.srec");
g_assert (filename_srec != NULL);
file_srec = g_file_new_for_path (filename_srec);
data_srec = g_file_load_bytes (file_srec, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (data_srec != NULL);
ret = fu_firmware_parse (firmware, data_srec, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error (error);
g_assert (ret);
data_bin = fu_firmware_get_image_default_bytes (firmware, &error);
g_assert_no_error (error);
g_assert_nonnull (data_bin);
g_assert_cmpint (g_bytes_get_size (data_bin), ==, 136);
/* did we match the reference file? */
filename_ref = fu_test_get_filename (TESTDATADIR, "firmware.bin");
g_assert (filename_ref != NULL);
file_bin = g_file_new_for_path (filename_ref);
data_ref = g_file_load_bytes (file_bin, NULL, NULL, &error);
g_assert_no_error (error);
g_assert (data_ref != NULL);
ret = fu_common_bytes_compare (data_bin, data_ref, &error);
g_assert_no_error (error);
g_assert_true (ret);
}
static void
fu_firmware_func (void)
{
g_autoptr(FuFirmware) firmware = fu_firmware_new ();
g_autoptr(FuFirmwareImage) img1 = fu_firmware_image_new (NULL);
g_autoptr(FuFirmwareImage) img2 = fu_firmware_image_new (NULL);
g_autoptr(FuFirmwareImage) img_id = NULL;
g_autoptr(FuFirmwareImage) img_idx = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *str = NULL;
fu_firmware_image_set_addr (img1, 0x200);
fu_firmware_image_set_idx (img1, 13);
fu_firmware_image_set_id (img1, "primary");
fu_firmware_add_image (firmware, img1);
fu_firmware_image_set_addr (img2, 0x400);
fu_firmware_image_set_idx (img2, 23);
fu_firmware_image_set_id (img2, "secondary");
fu_firmware_add_image (firmware, img2);
img_id = fu_firmware_get_image_by_id (firmware, "NotGoingToExist", &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_null (img_id);
g_clear_error (&error);
img_id = fu_firmware_get_image_by_id (firmware, "primary", &error);
g_assert_no_error (error);
g_assert_nonnull (img_id);
g_assert_cmpint (fu_firmware_image_get_addr (img_id), ==, 0x200);
g_assert_cmpint (fu_firmware_image_get_idx (img_id), ==, 13);
g_assert_cmpstr (fu_firmware_image_get_id (img_id), ==, "primary");
img_idx = fu_firmware_get_image_by_idx (firmware, 123456, &error);
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_null (img_idx);
g_clear_error (&error);
img_idx = fu_firmware_get_image_by_idx (firmware, 23, &error);
g_assert_no_error (error);
g_assert_nonnull (img_idx);
g_assert_cmpint (fu_firmware_image_get_addr (img_idx), ==, 0x400);
g_assert_cmpint (fu_firmware_image_get_idx (img_idx), ==, 23);
g_assert_cmpstr (fu_firmware_image_get_id (img_idx), ==, "secondary");
str = fu_firmware_to_string (firmware);
g_assert_cmpstr (str, ==, "FuFirmware:\n"
" FuFirmwareImage:\n"
" ID: primary\n"
" Index: 0x000d\n"
" Address: 0x0200\n"
" FuFirmwareImage:\n"
" ID: secondary\n"
" Index: 0x0017\n"
" Address: 0x0400\n");
}
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -3685,6 +3929,11 @@ main (int argc, char **argv)
/* tests go here */ /* tests go here */
if (g_test_slow ()) if (g_test_slow ())
g_test_add_func ("/fwupd/progressbar", fu_progressbar_func); g_test_add_func ("/fwupd/progressbar", fu_progressbar_func);
g_test_add_func ("/fwupd/firmware", fu_firmware_func);
g_test_add_func ("/fwupd/firmware{ihex}", fu_firmware_ihex_func);
g_test_add_func ("/fwupd/firmware{ihex-offset}", fu_firmware_ihex_offset_func);
g_test_add_func ("/fwupd/firmware{ihex-signed}", fu_firmware_ihex_signed_func);
g_test_add_func ("/fwupd/firmware{srec}", fu_firmware_srec_func);
g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func); g_test_add_func ("/fwupd/archive{invalid}", fu_archive_invalid_func);
g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func); g_test_add_func ("/fwupd/archive{cab}", fu_archive_cab_func);
g_test_add_func ("/fwupd/engine{requirements-other-device}", fu_engine_requirements_other_device_func); g_test_add_func ("/fwupd/engine{requirements-other-device}", fu_engine_requirements_other_device_func);

303
src/fu-srec-firmware.c Normal file
View File

@ -0,0 +1,303 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuFirmware"
#include "config.h"
#include <string.h>
#include "fu-common.h"
#include "fu-firmware-common.h"
#include "fu-srec-firmware.h"
struct _FuSrecFirmware {
FuFirmware parent_instance;
};
G_DEFINE_TYPE (FuSrecFirmware, fu_srec_firmware, FU_TYPE_FIRMWARE)
static gboolean
fu_srec_firmware_parse (FuFirmware *firmware,
GBytes *fw,
guint64 addr_start,
guint64 addr_end,
FwupdInstallFlags flags,
GError **error)
{
const gchar *data;
gboolean got_eof = FALSE;
gboolean got_hdr = FALSE;
gsize sz = 0;
guint16 data_cnt = 0;
guint32 addr32_last = 0;
guint32 img_address = 0;
g_auto(GStrv) lines = NULL;
g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL);
g_autoptr(GBytes) img_bytes = NULL;
g_autoptr(GString) outbuf = g_string_new (NULL);
/* parse records */
data = g_bytes_get_data (fw, &sz);
lines = fu_common_strnsplit (data, sz, "\n", -1);
for (guint ln = 0; lines[ln] != NULL; ln++) {
const gchar *line = lines[ln];
gsize linesz;
guint32 rec_addr32;
guint8 addrsz = 0; /* bytes */
guint8 rec_count; /* words */
guint8 rec_kind;
/* ignore blank lines */
g_strdelimit (lines[ln], "\r", '\0');
linesz = strlen (line);
if (linesz == 0)
continue;
/* check starting token */
if (line[0] != 'S') {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid starting token, got '%c' at line %u",
line[0], ln);
return FALSE;
}
/* check there's enough data for the smallest possible record */
if (linesz < 10) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"record incomplete at line %u, length %u",
ln, (guint) linesz);
return FALSE;
}
/* kind, count, address, (data), checksum, linefeed */
rec_kind = line[1] - '0';
rec_count = fu_firmware_strparse_uint8 (line + 2);
if (rec_count * 2 != linesz - 4) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"count incomplete at line %u, "
"length %u, expected %u",
ln, (guint) linesz - 4, (guint) rec_count * 2);
return FALSE;
}
/* checksum check */
if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
guint8 rec_csum = 0;
guint8 rec_csum_expected;
for (guint8 i = 0; i < rec_count; i++)
rec_csum += fu_firmware_strparse_uint8 (line + (i * 2) + 2);
rec_csum ^= 0xff;
rec_csum_expected = fu_firmware_strparse_uint8 (line + (rec_count * 2) + 2);
if (rec_csum != rec_csum_expected) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"checksum incorrect line %u, "
"expected %02x, got %02x",
ln, rec_csum_expected, rec_csum);
return FALSE;
}
}
/* set each command settings */
switch (rec_kind) {
case 0:
addrsz = 2;
if (got_hdr) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"duplicate header record");
return FALSE;
}
got_hdr = TRUE;
break;
case 1:
addrsz = 2;
break;
case 2:
addrsz = 3;
break;
case 3:
addrsz = 4;
break;
case 5:
addrsz = 2;
got_eof = TRUE;
break;
case 6:
addrsz = 3;
break;
case 7:
addrsz = 4;
got_eof = TRUE;
break;
case 8:
addrsz = 3;
got_eof = TRUE;
break;
case 9:
addrsz = 2;
got_eof = TRUE;
break;
default:
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid srec record type S%c",
line[1]);
return FALSE;
}
/* parse address */
switch (addrsz) {
case 2:
rec_addr32 = fu_firmware_strparse_uint16 (line + 4);
break;
case 3:
rec_addr32 = fu_firmware_strparse_uint24 (line + 4);
break;
case 4:
rec_addr32 = fu_firmware_strparse_uint32 (line + 4);
break;
default:
g_assert_not_reached ();
}
/* header */
if (rec_kind == 0) {
g_autoptr(GString) modname = g_string_new (NULL);
if (rec_addr32 != 0x0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid header record address, got %04x",
rec_addr32);
return FALSE;
}
/* could be anything, lets assume text */
for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) {
guint8 tmp = fu_firmware_strparse_uint8 (line + i);
if (!g_ascii_isgraph (tmp))
break;
g_string_append_c (modname, tmp);
}
if (modname->len != 0)
fu_firmware_image_set_id (img, modname->str);
continue;
}
/* verify we got all records */
if (rec_kind == 5) {
if (rec_addr32 != data_cnt) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"count record was not valid, got 0x%02x expected 0x%02x",
(guint) rec_addr32, (guint) data_cnt);
return FALSE;
}
}
/* data */
if (rec_kind == 1 || rec_kind == 2 || rec_kind == 3) {
/* invalid */
if (!got_hdr) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"missing header record");
return FALSE;
}
/* does not make sense */
if (rec_addr32 < addr32_last) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"invalid address 0x%x, last was 0x%x",
(guint) rec_addr32,
(guint) addr32_last);
return FALSE;
}
if (rec_addr32 < addr_start) {
g_debug ("ignoring data at 0x%x as before start address 0x%x",
(guint) rec_addr32, (guint) addr_start);
} else {
guint bytecnt = 0;
guint32 len_hole = rec_addr32 - addr32_last;
/* fill any holes, but only up to 1Mb to avoid a DoS */
if (addr32_last > 0 && len_hole > 0x100000) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"hole of 0x%x bytes too large to fill",
(guint) len_hole);
return FALSE;
}
if (addr32_last > 0x0 && len_hole > 1) {
g_debug ("filling address 0x%08x to 0x%08x",
addr32_last + 1, addr32_last + len_hole - 1);
for (guint j = 0; j < len_hole; j++)
g_string_append_c (outbuf, 0xff);
}
/* add data */
for (guint8 i = 4 + (addrsz * 2); i <= rec_count * 2; i += 2) {
guint8 tmp = fu_firmware_strparse_uint8 (line + i);
g_string_append_c (outbuf, tmp);
bytecnt++;
}
if (img_address == 0x0)
img_address = rec_addr32;
addr32_last = rec_addr32 + bytecnt;
}
data_cnt++;
}
}
/* no EOF */
if (!got_eof) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"no EOF, perhaps truncated file");
return FALSE;
}
/* add single image */
img_bytes = g_bytes_new (outbuf->str, outbuf->len);
fu_firmware_image_set_bytes (img, img_bytes);
fu_firmware_image_set_addr (img, img_address);
fu_firmware_add_image (firmware, img);
return TRUE;
}
static void
fu_srec_firmware_init (FuSrecFirmware *self)
{
}
static void
fu_srec_firmware_class_init (FuSrecFirmwareClass *klass)
{
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
klass_firmware->parse = fu_srec_firmware_parse;
}
FuFirmware *
fu_srec_firmware_new (void)
{
return FU_FIRMWARE (g_object_new (FU_TYPE_SREC_FIRMWARE, NULL));
}

18
src/fu-srec-firmware.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include "fu-firmware.h"
G_BEGIN_DECLS
#define FU_TYPE_SREC_FIRMWARE (fu_srec_firmware_get_type ())
G_DECLARE_FINAL_TYPE (FuSrecFirmware, fu_srec_firmware, FU, SREC_FIRMWARE, FuFirmware)
FuFirmware *fu_srec_firmware_new (void);
G_END_DECLS

View File

@ -38,13 +38,18 @@ libfwupdprivate = static_library(
'fu-chunk.c', 'fu-chunk.c',
'fu-device.c', 'fu-device.c',
'fu-device-locker.c', 'fu-device-locker.c',
'fu-firmware.c',
'fu-firmware-common.c',
'fu-firmware-image.c',
'fu-hwids.c', 'fu-hwids.c',
'fu-history.c', 'fu-history.c',
'fu-ihex-firmware.c',
'fu-io-channel.c', 'fu-io-channel.c',
'fu-plugin.c', 'fu-plugin.c',
'fu-progressbar.c', 'fu-progressbar.c',
'fu-quirks.c', 'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
'fu-srec-firmware.c',
'fu-test.c', 'fu-test.c',
'fu-udev-device.c', 'fu-udev-device.c',
'fu-usb-device.c', 'fu-usb-device.c',
@ -203,6 +208,7 @@ fwupdtool = executable(
'fu-device-locker.c', 'fu-device-locker.c',
'fu-idle.c', 'fu-idle.c',
'fu-install-task.c', 'fu-install-task.c',
'fu-ihex-firmware.c',
'fu-io-channel.c', 'fu-io-channel.c',
'fu-keyring.c', 'fu-keyring.c',
'fu-keyring-utils.c', 'fu-keyring-utils.c',
@ -211,6 +217,7 @@ fwupdtool = executable(
'fu-plugin-list.c', 'fu-plugin-list.c',
'fu-quirks.c', 'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
'fu-srec-firmware.c',
'fu-udev-device.c', 'fu-udev-device.c',
'fu-usb-device.c', 'fu-usb-device.c',
'fu-util-common.c', 'fu-util-common.c',
@ -286,7 +293,11 @@ executable(
'fu-device.c', 'fu-device.c',
'fu-device-list.c', 'fu-device-list.c',
'fu-device-locker.c', 'fu-device-locker.c',
'fu-firmware.c',
'fu-firmware-common.c',
'fu-firmware-image.c',
'fu-idle.c', 'fu-idle.c',
'fu-ihex-firmware.c',
'fu-io-channel.c', 'fu-io-channel.c',
'fu-install-task.c', 'fu-install-task.c',
'fu-keyring.c', 'fu-keyring.c',
@ -296,6 +307,7 @@ executable(
'fu-plugin-list.c', 'fu-plugin-list.c',
'fu-quirks.c', 'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
'fu-srec-firmware.c',
'fu-udev-device.c', 'fu-udev-device.c',
'fu-usb-device.c', 'fu-usb-device.c',
], ],
@ -353,6 +365,9 @@ if get_option('tests')
'fu-common-version.c', 'fu-common-version.c',
'fu-config.c', 'fu-config.c',
'fu-engine.c', 'fu-engine.c',
'fu-firmware.c',
'fu-firmware-common.c',
'fu-firmware-image.c',
'fu-keyring.c', 'fu-keyring.c',
'fu-keyring-utils.c', 'fu-keyring-utils.c',
'fu-hwids.c', 'fu-hwids.c',
@ -361,6 +376,7 @@ if get_option('tests')
'fu-device-locker.c', 'fu-device-locker.c',
'fu-history.c', 'fu-history.c',
'fu-idle.c', 'fu-idle.c',
'fu-ihex-firmware.c',
'fu-install-task.c', 'fu-install-task.c',
'fu-io-channel.c', 'fu-io-channel.c',
'fu-keyring.c', 'fu-keyring.c',
@ -370,6 +386,7 @@ if get_option('tests')
'fu-progressbar.c', 'fu-progressbar.c',
'fu-quirks.c', 'fu-quirks.c',
'fu-smbios.c', 'fu-smbios.c',
'fu-srec-firmware.c',
'fu-test.c', 'fu-test.c',
'fu-udev-device.c', 'fu-udev-device.c',
'fu-usb-device.c', 'fu-usb-device.c',
@ -424,6 +441,12 @@ if get_option('introspection')
'fu-device.h', 'fu-device.h',
'fu-device-locker.c', 'fu-device-locker.c',
'fu-device-locker.h', 'fu-device-locker.h',
'fu-firmware.c',
'fu-firmware.h',
'fu-firmware-common.c',
'fu-firmware-common.h',
'fu-firmware-image.c',
'fu-firmware-image.h',
'fu-io-channel.c', 'fu-io-channel.c',
'fu-plugin.c', 'fu-plugin.c',
'fu-plugin.h', 'fu-plugin.h',