mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-07 02:53:02 +00:00
Tokenize the Intel Hex file before parsing
The raw lines are required for some unspecified hardware.
This commit is contained in:
parent
22c124dba4
commit
3d45e693cc
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
struct _FuIhexFirmware {
|
struct _FuIhexFirmware {
|
||||||
FuFirmware parent_instance;
|
FuFirmware parent_instance;
|
||||||
|
GPtrArray *records;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE)
|
G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE)
|
||||||
@ -28,6 +29,44 @@ G_DEFINE_TYPE (FuIhexFirmware, fu_ihex_firmware, FU_TYPE_FIRMWARE)
|
|||||||
#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05
|
#define DFU_INHX32_RECORD_TYPE_START_LINEAR 0x05
|
||||||
#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd
|
#define DFU_INHX32_RECORD_TYPE_SIGNATURE 0xfd
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fu_ihex_firmware_get_records:
|
||||||
|
* @self: A #FuIhexFirmware
|
||||||
|
*
|
||||||
|
* Returns the raw lines from tokenization.
|
||||||
|
*
|
||||||
|
* This might be useful if the plugin is expecting the hex file to be a list
|
||||||
|
* of operations, rather than a simple linear image with filled holes.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (element-type FuIhexFirmwareRecord): records
|
||||||
|
*
|
||||||
|
* Since: 1.3.4
|
||||||
|
**/
|
||||||
|
GPtrArray *
|
||||||
|
fu_ihex_firmware_get_records (FuIhexFirmware *self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (FU_IS_IHEX_FIRMWARE (self), NULL);
|
||||||
|
return self->records;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_ihex_firmware_record_free (FuIhexFirmwareRecord *rcd)
|
||||||
|
{
|
||||||
|
g_string_free (rcd->buf, TRUE);
|
||||||
|
g_free (rcd);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIhexFirmwareRecord, fu_ihex_firmware_record_free)
|
||||||
|
|
||||||
|
static FuIhexFirmwareRecord *
|
||||||
|
fu_ihex_firmware_record_new (guint ln, const gchar *buf)
|
||||||
|
{
|
||||||
|
FuIhexFirmwareRecord *rcd = g_new0 (FuIhexFirmwareRecord, 1);
|
||||||
|
rcd->ln = ln;
|
||||||
|
rcd->buf = g_string_new (buf);
|
||||||
|
return rcd;
|
||||||
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
fu_ihex_firmware_record_type_to_string (guint8 record_type)
|
fu_ihex_firmware_record_type_to_string (guint8 record_type)
|
||||||
{
|
{
|
||||||
@ -48,6 +87,24 @@ fu_ihex_firmware_record_type_to_string (guint8 record_type)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_ihex_firmware_tokenize (FuFirmware *firmware, GBytes *fw,
|
||||||
|
FwupdInstallFlags flags, GError **error)
|
||||||
|
{
|
||||||
|
FuIhexFirmware *self = FU_IHEX_FIRMWARE (firmware);
|
||||||
|
gsize sz = 0;
|
||||||
|
const gchar *data = g_bytes_get_data (fw, &sz);
|
||||||
|
g_auto(GStrv) lines = fu_common_strnsplit (data, sz, "\n", -1);
|
||||||
|
|
||||||
|
for (guint ln = 0; lines[ln] != NULL; ln++) {
|
||||||
|
g_autoptr(FuIhexFirmwareRecord) rcd = NULL;
|
||||||
|
g_strdelimit (lines[ln], "\r\x1a", '\0');
|
||||||
|
rcd = fu_ihex_firmware_record_new (ln + 1, lines[ln]);
|
||||||
|
g_ptr_array_add (self->records, g_steal_pointer (&rcd));
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fu_ihex_firmware_parse (FuFirmware *firmware,
|
fu_ihex_firmware_parse (FuFirmware *firmware,
|
||||||
GBytes *fw,
|
GBytes *fw,
|
||||||
@ -56,27 +113,21 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
FwupdInstallFlags flags,
|
FwupdInstallFlags flags,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
const gchar *data;
|
FuIhexFirmware *self = FU_IHEX_FIRMWARE (firmware);
|
||||||
gboolean got_eof = FALSE;
|
gboolean got_eof = FALSE;
|
||||||
gsize sz = 0;
|
|
||||||
guint32 abs_addr = 0x0;
|
guint32 abs_addr = 0x0;
|
||||||
guint32 addr_last = 0x0;
|
guint32 addr_last = 0x0;
|
||||||
guint32 img_addr = G_MAXUINT32;
|
guint32 img_addr = G_MAXUINT32;
|
||||||
guint32 seg_addr = 0x0;
|
guint32 seg_addr = 0x0;
|
||||||
g_auto(GStrv) lines = NULL;
|
|
||||||
g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL);
|
g_autoptr(FuFirmwareImage) img = fu_firmware_image_new (NULL);
|
||||||
g_autoptr(GBytes) img_bytes = NULL;
|
g_autoptr(GBytes) img_bytes = NULL;
|
||||||
g_autoptr(GByteArray) buf = g_byte_array_new ();
|
g_autoptr(GByteArray) buf = g_byte_array_new ();
|
||||||
g_autoptr(GByteArray) buf_signature = g_byte_array_new ();
|
g_autoptr(GByteArray) buf_signature = g_byte_array_new ();
|
||||||
|
|
||||||
g_return_val_if_fail (fw != NULL, FALSE);
|
|
||||||
|
|
||||||
/* parse records */
|
/* parse records */
|
||||||
data = g_bytes_get_data (fw, &sz);
|
for (guint k = 0; k < self->records->len; k++) {
|
||||||
lines = fu_common_strnsplit (data, sz, "\n", -1);
|
FuIhexFirmwareRecord *rcd = g_ptr_array_index (self->records, k);
|
||||||
for (guint ln = 0; lines[ln] != NULL; ln++) {
|
const gchar *line = rcd->buf->str;
|
||||||
const gchar *line = lines[ln];
|
|
||||||
gsize linesz;
|
|
||||||
guint32 addr;
|
guint32 addr;
|
||||||
guint8 byte_cnt;
|
guint8 byte_cnt;
|
||||||
guint8 record_type;
|
guint8 record_type;
|
||||||
@ -87,9 +138,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* ignore blank lines */
|
/* ignore blank lines */
|
||||||
g_strdelimit (lines[ln], "\r\x1a", '\0');
|
if (rcd->buf->len == 0)
|
||||||
linesz = strlen (line);
|
|
||||||
if (linesz == 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* check starting token */
|
/* check starting token */
|
||||||
@ -98,17 +147,17 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
FWUPD_ERROR,
|
FWUPD_ERROR,
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
"invalid starting token on line %u: %s",
|
"invalid starting token on line %u: %s",
|
||||||
ln + 1, line);
|
rcd->ln, line);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check there's enough data for the smallest possible record */
|
/* check there's enough data for the smallest possible record */
|
||||||
if (linesz < 11) {
|
if (rcd->buf->len < 11) {
|
||||||
g_set_error (error,
|
g_set_error (error,
|
||||||
FWUPD_ERROR,
|
FWUPD_ERROR,
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
"line %u is incomplete, length %u",
|
"line %u is incomplete, length %u",
|
||||||
ln + 1, (guint) linesz);
|
rcd->ln, (guint) rcd->buf->len);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,12 +174,12 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
|
|
||||||
/* position of checksum */
|
/* position of checksum */
|
||||||
line_end = 9 + byte_cnt * 2;
|
line_end = 9 + byte_cnt * 2;
|
||||||
if (line_end > (guint) linesz) {
|
if (line_end > (guint) rcd->buf->len) {
|
||||||
g_set_error (error,
|
g_set_error (error,
|
||||||
FWUPD_ERROR,
|
FWUPD_ERROR,
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
"line %u malformed, length: %u",
|
"line %u malformed, length: %u",
|
||||||
ln + 1, line_end);
|
rcd->ln, line_end);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +195,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
FWUPD_ERROR,
|
FWUPD_ERROR,
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
"line %u has invalid checksum (0x%02x)",
|
"line %u has invalid checksum (0x%02x)",
|
||||||
ln + 1, checksum);
|
rcd->ln, checksum);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +215,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
"invalid address 0x%x, last was 0x%x on line %u",
|
"invalid address 0x%x, last was 0x%x on line %u",
|
||||||
(guint) addr,
|
(guint) addr,
|
||||||
(guint) addr_last,
|
(guint) addr_last,
|
||||||
ln + 1);
|
rcd->ln);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,12 +231,12 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
FWUPD_ERROR_INVALID_FILE,
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
"hole of 0x%x bytes too large to fill on line %u",
|
"hole of 0x%x bytes too large to fill on line %u",
|
||||||
(guint) len_hole,
|
(guint) len_hole,
|
||||||
ln + 1);
|
rcd->ln);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (addr_last > 0x0 && len_hole > 1) {
|
if (addr_last > 0x0 && len_hole > 1) {
|
||||||
g_debug ("filling address 0x%08x to 0x%08x on line %u",
|
g_debug ("filling address 0x%08x to 0x%08x on line %u",
|
||||||
addr_last + 1, addr_last + len_hole - 1, ln + 1);
|
addr_last + 1, addr_last + len_hole - 1, rcd->ln);
|
||||||
for (guint j = 1; j < len_hole; j++) {
|
for (guint j = 1; j < len_hole; j++) {
|
||||||
/* although 0xff might be clearer,
|
/* although 0xff might be clearer,
|
||||||
* we can't write 0xffff to pic14 */
|
* we can't write 0xffff to pic14 */
|
||||||
@ -213,21 +262,21 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
break;
|
break;
|
||||||
case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR:
|
case DFU_INHX32_RECORD_TYPE_EXTENDED_LINEAR:
|
||||||
abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16;
|
abs_addr = fu_firmware_strparse_uint16 (line + 9) << 16;
|
||||||
g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, ln + 1);
|
g_debug (" abs_addr:\t0x%02x on line %u", abs_addr, rcd->ln);
|
||||||
break;
|
break;
|
||||||
case DFU_INHX32_RECORD_TYPE_START_LINEAR:
|
case DFU_INHX32_RECORD_TYPE_START_LINEAR:
|
||||||
abs_addr = fu_firmware_strparse_uint32 (line + 9);
|
abs_addr = fu_firmware_strparse_uint32 (line + 9);
|
||||||
g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, ln + 1);
|
g_debug (" abs_addr:\t0x%08x on line %u", abs_addr, rcd->ln);
|
||||||
break;
|
break;
|
||||||
case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT:
|
case DFU_INHX32_RECORD_TYPE_EXTENDED_SEGMENT:
|
||||||
/* segment base address, so ~1Mb addressable */
|
/* segment base address, so ~1Mb addressable */
|
||||||
seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16;
|
seg_addr = fu_firmware_strparse_uint16 (line + 9) * 16;
|
||||||
g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, ln + 1);
|
g_debug (" seg_addr:\t0x%08x on line %u", seg_addr, rcd->ln);
|
||||||
break;
|
break;
|
||||||
case DFU_INHX32_RECORD_TYPE_START_SEGMENT:
|
case DFU_INHX32_RECORD_TYPE_START_SEGMENT:
|
||||||
/* initial content of the CS:IP registers */
|
/* initial content of the CS:IP registers */
|
||||||
seg_addr = fu_firmware_strparse_uint32 (line + 9);
|
seg_addr = fu_firmware_strparse_uint32 (line + 9);
|
||||||
g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, ln + 1);
|
g_debug (" seg_addr:\t0x%02x on line %u", seg_addr, rcd->ln);
|
||||||
break;
|
break;
|
||||||
case DFU_INHX32_RECORD_TYPE_SIGNATURE:
|
case DFU_INHX32_RECORD_TYPE_SIGNATURE:
|
||||||
for (guint i = 9; i < line_end; i += 2) {
|
for (guint i = 9; i < line_end; i += 2) {
|
||||||
@ -243,7 +292,7 @@ fu_ihex_firmware_parse (FuFirmware *firmware,
|
|||||||
FWUPD_ERROR,
|
FWUPD_ERROR,
|
||||||
FWUPD_ERROR_INVALID_FILE,
|
FWUPD_ERROR_INVALID_FILE,
|
||||||
"invalid ihex record type %i on line %u",
|
"invalid ihex record type %i on line %u",
|
||||||
record_type, ln + 1);
|
record_type, rcd->ln);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,16 +409,28 @@ fu_ihex_firmware_write (FuFirmware *firmware, GError **error)
|
|||||||
return g_bytes_new (str->str, str->len);
|
return g_bytes_new (str->str, str->len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_ihex_firmware_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
FuIhexFirmware *self = FU_IHEX_FIRMWARE (object);
|
||||||
|
g_ptr_array_unref (self->records);
|
||||||
|
G_OBJECT_CLASS (fu_ihex_firmware_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_ihex_firmware_init (FuIhexFirmware *self)
|
fu_ihex_firmware_init (FuIhexFirmware *self)
|
||||||
{
|
{
|
||||||
|
self->records = g_ptr_array_new_with_free_func ((GFreeFunc) fu_ihex_firmware_record_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_ihex_firmware_class_init (FuIhexFirmwareClass *klass)
|
fu_ihex_firmware_class_init (FuIhexFirmwareClass *klass)
|
||||||
{
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
|
FuFirmwareClass *klass_firmware = FU_FIRMWARE_CLASS (klass);
|
||||||
|
object_class->finalize = fu_ihex_firmware_finalize;
|
||||||
klass_firmware->parse = fu_ihex_firmware_parse;
|
klass_firmware->parse = fu_ihex_firmware_parse;
|
||||||
|
klass_firmware->tokenize = fu_ihex_firmware_tokenize;
|
||||||
klass_firmware->write = fu_ihex_firmware_write;
|
klass_firmware->write = fu_ihex_firmware_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,4 +11,10 @@
|
|||||||
#define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type ())
|
#define FU_TYPE_IHEX_FIRMWARE (fu_ihex_firmware_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware)
|
G_DECLARE_FINAL_TYPE (FuIhexFirmware, fu_ihex_firmware, FU, IHEX_FIRMWARE, FuFirmware)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint ln;
|
||||||
|
GString *buf;
|
||||||
|
} FuIhexFirmwareRecord;
|
||||||
|
|
||||||
FuFirmware *fu_ihex_firmware_new (void);
|
FuFirmware *fu_ihex_firmware_new (void);
|
||||||
|
GPtrArray *fu_ihex_firmware_get_records (FuIhexFirmware *self);
|
||||||
|
Loading…
Reference in New Issue
Block a user