Coverage Report

Created: 2026-07-30 07:09

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.01k
G_DEFINE_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU_TYPE_FIRMWARE)
18
1.01k
19
1.01k
static void
20
1.01k
fu_elanfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
21
1.01k
{
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
          FuInputStream *stream,
44
          gsize offset,
45
          GError **error)
46
486
{
47
486
  return fu_struct_elanfp_firmware_hdr_validate_stream(stream, offset, error);
48
486
}
49
50
static gboolean
51
fu_elanfp_firmware_parse(FuFirmware *firmware,
52
       FuInputStream *stream,
53
       FuFirmwareParseFlags flags,
54
       GError **error)
55
427
{
56
427
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
57
427
  gsize offset = 0;
58
59
  /* file format version */
60
427
  if (!fu_input_stream_read_u32(stream,
61
427
              offset + 0x4,
62
427
              &self->format_version,
63
427
              G_LITTLE_ENDIAN,
64
427
              error))
65
3
    return FALSE;
66
67
  /* read indexes */
68
424
  if (!fu_size_checked_inc(&offset, 0x10, error))
69
0
    return FALSE;
70
907
  while (1) {
71
907
    guint32 start_addr = 0;
72
907
    guint32 length = 0;
73
907
    guint32 fwtype = 0;
74
907
    g_autoptr(FuFirmware) img = NULL;
75
907
    g_autoptr(FuInputStream) stream_tmp = NULL;
76
77
    /* type, reserved, start-addr, len */
78
907
    if (!fu_input_stream_read_u32(stream,
79
907
                offset + 0x0,
80
907
                &fwtype,
81
907
                G_LITTLE_ENDIAN,
82
907
                error))
83
34
      return FALSE;
84
85
    /* check not already added */
86
873
    img = fu_firmware_get_image_by_idx(firmware, fwtype, NULL);
87
873
    if (img != NULL) {
88
5
      g_set_error(error,
89
5
            FWUPD_ERROR,
90
5
            FWUPD_ERROR_NOT_SUPPORTED,
91
5
            "already parsed image with fwtype 0x%x",
92
5
            fwtype);
93
5
      return FALSE;
94
5
    }
95
96
    /* done */
97
868
    if (fwtype == FU_ELANTP_FIRMWARE_IDX_END)
98
101
      break;
99
767
    switch (fwtype) {
100
52
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A:
101
101
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B:
102
101
      img = fu_cfu_offer_new();
103
101
      break;
104
282
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A:
105
502
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B:
106
502
      img = fu_cfu_payload_new();
107
502
      break;
108
164
    default:
109
164
      img = fu_firmware_new();
110
164
      break;
111
767
    }
112
767
    fu_firmware_set_idx(img, fwtype);
113
767
    if (!fu_input_stream_read_u32(stream,
114
767
                offset + 0x8,
115
767
                &start_addr,
116
767
                G_LITTLE_ENDIAN,
117
767
                error))
118
57
      return FALSE;
119
710
    fu_firmware_set_addr(img, start_addr);
120
710
    if (!fu_input_stream_read_u32(stream,
121
710
                offset + 0xC,
122
710
                &length,
123
710
                G_LITTLE_ENDIAN,
124
710
                error))
125
13
      return FALSE;
126
697
    if (length == 0) {
127
14
      g_set_error(error,
128
14
            FWUPD_ERROR,
129
14
            FWUPD_ERROR_NOT_SUPPORTED,
130
14
            "zero size fwtype 0x%x not supported",
131
14
            fwtype);
132
14
      return FALSE;
133
14
    }
134
135
683
    stream_tmp = fu_partial_input_stream_new(stream, start_addr, length, error);
136
683
    if (stream_tmp == NULL)
137
117
      return FALSE;
138
566
    if (!fu_firmware_parse_stream(img,
139
566
                stream_tmp,
140
566
                0x0,
141
566
                flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
142
566
                error))
143
70
      return FALSE;
144
496
    if (!fu_firmware_add_image(firmware, img, error))
145
13
      return FALSE;
146
147
483
    if (!fu_size_checked_inc(&offset, 0x10, error)) {
148
0
      g_prefix_error_literal(error, "index offset overflow: ");
149
0
      return FALSE;
150
0
    }
151
483
  }
152
153
  /* success */
154
101
  return TRUE;
155
424
}
156
157
static GByteArray *
158
fu_elanfp_firmware_write(FuFirmware *firmware, GError **error)
159
101
{
160
101
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
161
101
  gsize offset = 0;
162
101
  g_autoptr(GByteArray) buf = g_byte_array_new();
163
101
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
164
165
  /* S2F_HEADER */
166
101
  fu_byte_array_append_uint32(buf, 0x46325354, G_LITTLE_ENDIAN);
167
101
  fu_byte_array_append_uint32(buf, self->format_version, G_LITTLE_ENDIAN);
168
101
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* ICID, assumed */
169
101
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
170
171
  /* S2F_INDEX */
172
101
  if (!fu_size_checked_inc_product(&offset, (imgs->len + 1), 0x10, error)) {
173
0
    g_prefix_error_literal(error, "index size overflow: ");
174
0
    return NULL;
175
0
  }
176
101
  if (!fu_size_checked_inc(&offset, 0x10, error)) {
177
0
    g_prefix_error_literal(error, "header offset overflow: ");
178
0
    return NULL;
179
0
  }
180
181
288
  for (guint i = 0; i < imgs->len; i++) {
182
187
    FuFirmware *img = g_ptr_array_index(imgs, i);
183
187
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
184
187
    if (blob == NULL)
185
0
      return NULL;
186
187
    fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN);
187
187
    fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
188
187
    fu_byte_array_append_uint32(buf, offset, G_LITTLE_ENDIAN);
189
187
    fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN);
190
187
    if (!fu_size_checked_inc(&offset, g_bytes_get_size(blob), error)) {
191
0
      g_prefix_error(error, "image %u offset overflow: ", i);
192
0
      return NULL;
193
0
    }
194
187
  }
195
196
  /* end of index */
197
101
  fu_byte_array_append_uint32(buf, FU_ELANTP_FIRMWARE_IDX_END, G_LITTLE_ENDIAN);
198
101
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
199
101
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
200
101
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
201
202
  /* data */
203
288
  for (guint i = 0; i < imgs->len; i++) {
204
187
    FuFirmware *img = g_ptr_array_index(imgs, i);
205
187
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
206
187
    if (blob == NULL)
207
0
      return NULL;
208
187
    fu_byte_array_append_bytes(buf, blob);
209
187
  }
210
211
  /* success */
212
101
  return g_steal_pointer(&buf);
213
101
}
214
215
static void
216
fu_elanfp_firmware_init(FuElanfpFirmware *self)
217
486
{
218
486
}
219
220
static void
221
fu_elanfp_firmware_class_init(FuElanfpFirmwareClass *klass)
222
1
{
223
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
224
1
  fu_firmware_add_image_gtype(firmware_class, FU_TYPE_CFU_OFFER);
225
1
  fu_firmware_add_image_gtype(firmware_class, FU_TYPE_CFU_PAYLOAD);
226
1
  firmware_class->validate = fu_elanfp_firmware_validate;
227
1
  firmware_class->parse = fu_elanfp_firmware_parse;
228
1
  firmware_class->write = fu_elanfp_firmware_write;
229
1
  firmware_class->export = fu_elanfp_firmware_export;
230
1
  firmware_class->build = fu_elanfp_firmware_build;
231
1
  fu_firmware_set_images_max(firmware_class, 256);
232
1
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
233
1
}