Coverage Report

Created: 2025-11-11 06:44

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.45k
G_DEFINE_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU_TYPE_FIRMWARE)
18
1.45k
19
1.45k
static void
20
1.45k
fu_elanfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
21
1.45k
{
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
506k
{
47
506k
  return fu_struct_elanfp_firmware_hdr_validate_stream(stream, offset, error);
48
506k
}
49
50
static gboolean
51
fu_elanfp_firmware_parse(FuFirmware *firmware,
52
       GInputStream *stream,
53
       FuFirmwareParseFlags flags,
54
       GError **error)
55
598
{
56
598
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
57
598
  gsize offset = 0;
58
59
  /* file format version */
60
598
  if (!fu_input_stream_read_u32(stream,
61
598
              offset + 0x4,
62
598
              &self->format_version,
63
598
              G_LITTLE_ENDIAN,
64
598
              error))
65
37
    return FALSE;
66
67
  /* read indexes */
68
561
  offset += 0x10;
69
5.63k
  while (1) {
70
5.63k
    guint32 start_addr = 0;
71
5.63k
    guint32 length = 0;
72
5.63k
    guint32 fwtype = 0;
73
5.63k
    g_autoptr(FuFirmware) img = NULL;
74
5.63k
    g_autoptr(GInputStream) stream_tmp = NULL;
75
76
    /* type, reserved, start-addr, len */
77
5.63k
    if (!fu_input_stream_read_u32(stream,
78
5.63k
                offset + 0x0,
79
5.63k
                &fwtype,
80
5.63k
                G_LITTLE_ENDIAN,
81
5.63k
                error))
82
113
      return FALSE;
83
84
    /* check not already added */
85
5.52k
    img = fu_firmware_get_image_by_idx(firmware, fwtype, NULL);
86
5.52k
    if (img != NULL) {
87
5
      g_set_error(error,
88
5
            FWUPD_ERROR,
89
5
            FWUPD_ERROR_NOT_SUPPORTED,
90
5
            "already parsed image with fwtype 0x%x",
91
5
            fwtype);
92
5
      return FALSE;
93
5
    }
94
95
    /* done */
96
5.51k
    if (fwtype == FU_ELANTP_FIRMWARE_IDX_END)
97
138
      break;
98
5.37k
    switch (fwtype) {
99
64
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A:
100
129
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B:
101
129
      img = fu_cfu_offer_new();
102
129
      break;
103
277
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A:
104
516
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B:
105
516
      img = fu_cfu_payload_new();
106
516
      break;
107
4.73k
    default:
108
4.73k
      img = fu_firmware_new();
109
4.73k
      break;
110
5.37k
    }
111
5.37k
    fu_firmware_set_idx(img, fwtype);
112
5.37k
    if (!fu_input_stream_read_u32(stream,
113
5.37k
                offset + 0x8,
114
5.37k
                &start_addr,
115
5.37k
                G_LITTLE_ENDIAN,
116
5.37k
                error))
117
93
      return FALSE;
118
5.28k
    fu_firmware_set_addr(img, start_addr);
119
5.28k
    if (!fu_input_stream_read_u32(stream,
120
5.28k
                offset + 0xC,
121
5.28k
                &length,
122
5.28k
                G_LITTLE_ENDIAN,
123
5.28k
                error))
124
22
      return FALSE;
125
5.26k
    if (length == 0) {
126
7
      g_set_error(error,
127
7
            FWUPD_ERROR,
128
7
            FWUPD_ERROR_NOT_SUPPORTED,
129
7
            "zero size fwtype 0x%x not supported",
130
7
            fwtype);
131
7
      return FALSE;
132
7
    }
133
134
5.25k
    stream_tmp = fu_partial_input_stream_new(stream, start_addr, length, error);
135
5.25k
    if (stream_tmp == NULL)
136
133
      return FALSE;
137
5.12k
    if (!fu_firmware_parse_stream(img,
138
5.12k
                stream_tmp,
139
5.12k
                0x0,
140
5.12k
                flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
141
5.12k
                error))
142
50
      return FALSE;
143
5.07k
    if (!fu_firmware_add_image(firmware, img, error))
144
0
      return FALSE;
145
146
5.07k
    offset += 0x10;
147
5.07k
  }
148
149
  /* success */
150
138
  return TRUE;
151
561
}
152
153
static GByteArray *
154
fu_elanfp_firmware_write(FuFirmware *firmware, GError **error)
155
138
{
156
138
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
157
138
  gsize offset = 0;
158
138
  g_autoptr(GByteArray) buf = g_byte_array_new();
159
138
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
160
161
  /* S2F_HEADER */
162
138
  fu_byte_array_append_uint32(buf, 0x46325354, G_LITTLE_ENDIAN);
163
138
  fu_byte_array_append_uint32(buf, self->format_version, G_LITTLE_ENDIAN);
164
138
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* ICID, assumed */
165
138
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
166
167
  /* S2F_INDEX */
168
138
  offset += 0x10 + ((imgs->len + 1) * 0x10);
169
361
  for (guint i = 0; i < imgs->len; i++) {
170
269
    FuFirmware *img = g_ptr_array_index(imgs, i);
171
269
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
172
269
    if (blob == NULL)
173
46
      return NULL;
174
223
    fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN);
175
223
    fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
176
223
    fu_byte_array_append_uint32(buf, offset, G_LITTLE_ENDIAN);
177
223
    fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN);
178
223
    offset += g_bytes_get_size(blob);
179
223
  }
180
181
  /* end of index */
182
92
  fu_byte_array_append_uint32(buf, FU_ELANTP_FIRMWARE_IDX_END, G_LITTLE_ENDIAN);
183
92
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
184
92
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
185
92
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
186
187
  /* data */
188
261
  for (guint i = 0; i < imgs->len; i++) {
189
169
    FuFirmware *img = g_ptr_array_index(imgs, i);
190
169
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
191
169
    if (blob == NULL)
192
0
      return NULL;
193
169
    fu_byte_array_append_bytes(buf, blob);
194
169
  }
195
196
  /* success */
197
92
  return g_steal_pointer(&buf);
198
92
}
199
200
static void
201
fu_elanfp_firmware_init(FuElanfpFirmware *self)
202
716
{
203
716
  fu_firmware_set_images_max(FU_FIRMWARE(self), 256);
204
716
  g_type_ensure(FU_TYPE_CFU_OFFER);
205
716
  g_type_ensure(FU_TYPE_CFU_PAYLOAD);
206
716
}
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
}