Coverage Report

Created: 2025-11-09 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/elanfp/fu-elanfp-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2021 Michael Cheng <michael.cheng@emc.com.tw>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-elanfp-firmware.h"
10
#include "fu-elanfp-struct.h"
11
12
struct _FuElanfpFirmware {
13
  FuFirmware parent_instance;
14
  guint32 format_version;
15
};
16
17
1.41k
G_DEFINE_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU_TYPE_FIRMWARE)
18
1.41k
19
1.41k
static void
20
1.41k
fu_elanfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
21
1.41k
{
22
0
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
23
0
  fu_xmlb_builder_insert_kx(bn, "format_version", self->format_version);
24
0
}
25
26
static gboolean
27
fu_elanfp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
28
0
{
29
0
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
30
0
  guint64 tmp;
31
32
  /* optional properties */
33
0
  tmp = xb_node_query_text_as_uint(n, "format_version", NULL);
34
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
35
0
    self->format_version = tmp;
36
37
  /* success */
38
0
  return TRUE;
39
0
}
40
41
static gboolean
42
fu_elanfp_firmware_validate(FuFirmware *firmware,
43
          GInputStream *stream,
44
          gsize offset,
45
          GError **error)
46
316k
{
47
316k
  return fu_struct_elanfp_firmware_hdr_validate_stream(stream, offset, error);
48
316k
}
49
50
static gboolean
51
fu_elanfp_firmware_parse(FuFirmware *firmware,
52
       GInputStream *stream,
53
       FuFirmwareParseFlags flags,
54
       GError **error)
55
570
{
56
570
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
57
570
  gsize offset = 0;
58
59
  /* file format version */
60
570
  if (!fu_input_stream_read_u32(stream,
61
570
              offset + 0x4,
62
570
              &self->format_version,
63
570
              G_LITTLE_ENDIAN,
64
570
              error))
65
31
    return FALSE;
66
67
  /* read indexes */
68
539
  offset += 0x10;
69
5.64k
  while (1) {
70
5.64k
    guint32 start_addr = 0;
71
5.64k
    guint32 length = 0;
72
5.64k
    guint32 fwtype = 0;
73
5.64k
    g_autoptr(FuFirmware) img = NULL;
74
5.64k
    g_autoptr(GInputStream) stream_tmp = NULL;
75
76
    /* type, reserved, start-addr, len */
77
5.64k
    if (!fu_input_stream_read_u32(stream,
78
5.64k
                offset + 0x0,
79
5.64k
                &fwtype,
80
5.64k
                G_LITTLE_ENDIAN,
81
5.64k
                error))
82
111
      return FALSE;
83
84
    /* check not already added */
85
5.53k
    img = fu_firmware_get_image_by_idx(firmware, fwtype, NULL);
86
5.53k
    if (img != NULL) {
87
4
      g_set_error(error,
88
4
            FWUPD_ERROR,
89
4
            FWUPD_ERROR_NOT_SUPPORTED,
90
4
            "already parsed image with fwtype 0x%x",
91
4
            fwtype);
92
4
      return FALSE;
93
4
    }
94
95
    /* done */
96
5.53k
    if (fwtype == FU_ELANTP_FIRMWARE_IDX_END)
97
134
      break;
98
5.39k
    switch (fwtype) {
99
64
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A:
100
128
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B:
101
128
      img = fu_cfu_offer_new();
102
128
      break;
103
273
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A:
104
506
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B:
105
506
      img = fu_cfu_payload_new();
106
506
      break;
107
4.76k
    default:
108
4.76k
      img = fu_firmware_new();
109
4.76k
      break;
110
5.39k
    }
111
5.39k
    fu_firmware_set_idx(img, fwtype);
112
5.39k
    if (!fu_input_stream_read_u32(stream,
113
5.39k
                offset + 0x8,
114
5.39k
                &start_addr,
115
5.39k
                G_LITTLE_ENDIAN,
116
5.39k
                error))
117
98
      return FALSE;
118
5.30k
    fu_firmware_set_addr(img, start_addr);
119
5.30k
    if (!fu_input_stream_read_u32(stream,
120
5.30k
                offset + 0xC,
121
5.30k
                &length,
122
5.30k
                G_LITTLE_ENDIAN,
123
5.30k
                error))
124
22
      return FALSE;
125
5.27k
    if (length == 0) {
126
6
      g_set_error(error,
127
6
            FWUPD_ERROR,
128
6
            FWUPD_ERROR_NOT_SUPPORTED,
129
6
            "zero size fwtype 0x%x not supported",
130
6
            fwtype);
131
6
      return FALSE;
132
6
    }
133
134
5.27k
    stream_tmp = fu_partial_input_stream_new(stream, start_addr, length, error);
135
5.27k
    if (stream_tmp == NULL)
136
119
      return FALSE;
137
5.15k
    if (!fu_firmware_parse_stream(img,
138
5.15k
                stream_tmp,
139
5.15k
                0x0,
140
5.15k
                flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
141
5.15k
                error))
142
45
      return FALSE;
143
5.10k
    if (!fu_firmware_add_image(firmware, img, error))
144
0
      return FALSE;
145
146
5.10k
    offset += 0x10;
147
5.10k
  }
148
149
  /* success */
150
134
  return TRUE;
151
539
}
152
153
static GByteArray *
154
fu_elanfp_firmware_write(FuFirmware *firmware, GError **error)
155
134
{
156
134
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
157
134
  gsize offset = 0;
158
134
  g_autoptr(GByteArray) buf = g_byte_array_new();
159
134
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
160
161
  /* S2F_HEADER */
162
134
  fu_byte_array_append_uint32(buf, 0x46325354, G_LITTLE_ENDIAN);
163
134
  fu_byte_array_append_uint32(buf, self->format_version, G_LITTLE_ENDIAN);
164
134
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* ICID, assumed */
165
134
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
166
167
  /* S2F_INDEX */
168
134
  offset += 0x10 + ((imgs->len + 1) * 0x10);
169
350
  for (guint i = 0; i < imgs->len; i++) {
170
262
    FuFirmware *img = g_ptr_array_index(imgs, i);
171
262
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
172
262
    if (blob == NULL)
173
46
      return NULL;
174
216
    fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN);
175
216
    fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
176
216
    fu_byte_array_append_uint32(buf, offset, G_LITTLE_ENDIAN);
177
216
    fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN);
178
216
    offset += g_bytes_get_size(blob);
179
216
  }
180
181
  /* end of index */
182
88
  fu_byte_array_append_uint32(buf, FU_ELANTP_FIRMWARE_IDX_END, G_LITTLE_ENDIAN);
183
88
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
184
88
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
185
88
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
186
187
  /* data */
188
249
  for (guint i = 0; i < imgs->len; i++) {
189
161
    FuFirmware *img = g_ptr_array_index(imgs, i);
190
161
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
191
161
    if (blob == NULL)
192
0
      return NULL;
193
161
    fu_byte_array_append_bytes(buf, blob);
194
161
  }
195
196
  /* success */
197
88
  return g_steal_pointer(&buf);
198
88
}
199
200
static void
201
fu_elanfp_firmware_init(FuElanfpFirmware *self)
202
715
{
203
715
  fu_firmware_set_images_max(FU_FIRMWARE(self), 256);
204
715
  g_type_ensure(FU_TYPE_CFU_OFFER);
205
715
  g_type_ensure(FU_TYPE_CFU_PAYLOAD);
206
715
}
207
208
static void
209
fu_elanfp_firmware_class_init(FuElanfpFirmwareClass *klass)
210
1
{
211
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
212
1
  firmware_class->validate = fu_elanfp_firmware_validate;
213
1
  firmware_class->parse = fu_elanfp_firmware_parse;
214
1
  firmware_class->write = fu_elanfp_firmware_write;
215
1
  firmware_class->export = fu_elanfp_firmware_export;
216
1
  firmware_class->build = fu_elanfp_firmware_build;
217
1
}