Coverage Report

Created: 2025-07-01 07:09

/src/fwupd/plugins/elanfp/fu-elanfp-firmware.c
Line
Count
Source (jump to first uncovered line)
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
  FuFirmwareClass parent_instance;
14
  guint32 format_version;
15
};
16
17
G_DEFINE_TYPE(FuElanfpFirmware, fu_elanfp_firmware, FU_TYPE_FIRMWARE)
18
19
static void
20
fu_elanfp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
21
0
{
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
14.3M
{
47
14.3M
  return fu_struct_elanfp_firmware_hdr_validate_stream(stream, offset, error);
48
14.3M
}
49
50
static gboolean
51
fu_elanfp_firmware_parse(FuFirmware *firmware,
52
       GInputStream *stream,
53
       FuFirmwareParseFlags flags,
54
       GError **error)
55
752
{
56
752
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
57
752
  gsize offset = 0;
58
59
  /* file format version */
60
752
  if (!fu_input_stream_read_u32(stream,
61
752
              offset + 0x4,
62
752
              &self->format_version,
63
752
              G_LITTLE_ENDIAN,
64
752
              error))
65
41
    return FALSE;
66
67
  /* read indexes */
68
711
  offset += 0x10;
69
7.14k
  while (1) {
70
7.14k
    guint32 start_addr = 0;
71
7.14k
    guint32 length = 0;
72
7.14k
    guint32 fwtype = 0;
73
7.14k
    g_autoptr(FuFirmware) img = NULL;
74
7.14k
    g_autoptr(GInputStream) stream_tmp = NULL;
75
76
    /* type, reserved, start-addr, len */
77
7.14k
    if (!fu_input_stream_read_u32(stream,
78
7.14k
                offset + 0x0,
79
7.14k
                &fwtype,
80
7.14k
                G_LITTLE_ENDIAN,
81
7.14k
                error))
82
156
      return FALSE;
83
84
    /* check not already added */
85
6.98k
    img = fu_firmware_get_image_by_idx(firmware, fwtype, NULL);
86
6.98k
    if (img != NULL) {
87
6
      g_set_error(error,
88
6
            FWUPD_ERROR,
89
6
            FWUPD_ERROR_NOT_SUPPORTED,
90
6
            "already parsed image with fwtype 0x%x",
91
6
            fwtype);
92
6
      return FALSE;
93
6
    }
94
95
    /* done */
96
6.98k
    if (fwtype == FU_ELANTP_FIRMWARE_IDX_END)
97
156
      break;
98
6.82k
    switch (fwtype) {
99
112
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_A:
100
215
    case FU_ELANTP_FIRMWARE_IDX_CFU_OFFER_B:
101
215
      img = fu_cfu_offer_new();
102
215
      break;
103
320
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_A:
104
569
    case FU_ELANTP_FIRMWARE_IDX_CFU_PAYLOAD_B:
105
569
      img = fu_cfu_payload_new();
106
569
      break;
107
6.04k
    default:
108
6.04k
      img = fu_firmware_new();
109
6.04k
      break;
110
6.82k
    }
111
6.82k
    fu_firmware_set_idx(img, fwtype);
112
6.82k
    if (!fu_input_stream_read_u32(stream,
113
6.82k
                offset + 0x8,
114
6.82k
                &start_addr,
115
6.82k
                G_LITTLE_ENDIAN,
116
6.82k
                error))
117
132
      return FALSE;
118
6.69k
    fu_firmware_set_addr(img, start_addr);
119
6.69k
    if (!fu_input_stream_read_u32(stream,
120
6.69k
                offset + 0xC,
121
6.69k
                &length,
122
6.69k
                G_LITTLE_ENDIAN,
123
6.69k
                error))
124
23
      return FALSE;
125
6.67k
    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
6.66k
    stream_tmp = fu_partial_input_stream_new(stream, start_addr, length, error);
135
6.66k
    if (stream_tmp == NULL)
136
165
      return FALSE;
137
6.50k
    if (!fu_firmware_parse_stream(img,
138
6.50k
                stream_tmp,
139
6.50k
                0x0,
140
6.50k
                flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
141
6.50k
                error))
142
66
      return FALSE;
143
6.43k
    if (!fu_firmware_add_image_full(firmware, img, error))
144
0
      return FALSE;
145
146
6.43k
    offset += 0x10;
147
6.43k
  }
148
149
  /* success */
150
156
  return TRUE;
151
711
}
152
153
static GByteArray *
154
fu_elanfp_firmware_write(FuFirmware *firmware, GError **error)
155
156
{
156
156
  FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware);
157
156
  gsize offset = 0;
158
156
  g_autoptr(GByteArray) buf = g_byte_array_new();
159
156
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
160
161
  /* S2F_HEADER */
162
156
  fu_byte_array_append_uint32(buf, 0x46325354, G_LITTLE_ENDIAN);
163
156
  fu_byte_array_append_uint32(buf, self->format_version, G_LITTLE_ENDIAN);
164
156
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* ICID, assumed */
165
156
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
166
167
  /* S2F_INDEX */
168
156
  offset += 0x10 + ((imgs->len + 1) * 0x10);
169
393
  for (guint i = 0; i < imgs->len; i++) {
170
299
    FuFirmware *img = g_ptr_array_index(imgs, i);
171
299
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
172
299
    if (blob == NULL)
173
62
      return NULL;
174
237
    fu_byte_array_append_uint32(buf, fu_firmware_get_idx(img), G_LITTLE_ENDIAN);
175
237
    fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
176
237
    fu_byte_array_append_uint32(buf, offset, G_LITTLE_ENDIAN);
177
237
    fu_byte_array_append_uint32(buf, g_bytes_get_size(blob), G_LITTLE_ENDIAN);
178
237
    offset += g_bytes_get_size(blob);
179
237
  }
180
181
  /* end of index */
182
94
  fu_byte_array_append_uint32(buf, FU_ELANTP_FIRMWARE_IDX_END, G_LITTLE_ENDIAN);
183
94
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* reserved */
184
94
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
185
94
  fu_byte_array_append_uint32(buf, 0x0, G_LITTLE_ENDIAN); /* assumed */
186
187
  /* data */
188
268
  for (guint i = 0; i < imgs->len; i++) {
189
174
    FuFirmware *img = g_ptr_array_index(imgs, i);
190
174
    g_autoptr(GBytes) blob = fu_firmware_write(img, error);
191
174
    if (blob == NULL)
192
0
      return NULL;
193
174
    fu_byte_array_append_bytes(buf, blob);
194
174
  }
195
196
  /* success */
197
94
  return g_steal_pointer(&buf);
198
94
}
199
200
static void
201
fu_elanfp_firmware_init(FuElanfpFirmware *self)
202
901
{
203
901
  fu_firmware_set_images_max(FU_FIRMWARE(self), 256);
204
901
  g_type_ensure(FU_TYPE_CFU_OFFER);
205
901
  g_type_ensure(FU_TYPE_CFU_PAYLOAD);
206
901
}
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
}