Coverage Report

Created: 2026-04-09 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/elantp/fu-elantp-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2020 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-elantp-common.h"
10
#include "fu-elantp-firmware.h"
11
#include "fu-elantp-struct.h"
12
13
struct _FuElantpFirmware {
14
  FuFirmware parent_instance;
15
  guint16 module_id;
16
  guint16 ic_type;
17
  guint16 iap_addr;
18
  guint16 iap_ver;
19
  gboolean force_table_support;
20
  guint32 force_table_addr;
21
};
22
23
767
G_DEFINE_TYPE(FuElantpFirmware, fu_elantp_firmware, FU_TYPE_FIRMWARE)
24
767
25
823
#define FU_ELANTP_FIRMWARE_OFFSET_IAP 0x100 /* bytes */
26
27
guint16
28
fu_elantp_firmware_get_module_id(FuElantpFirmware *self)
29
0
{
30
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
31
0
  return self->module_id;
32
0
}
33
34
guint16
35
fu_elantp_firmware_get_ic_type(FuElantpFirmware *self)
36
0
{
37
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
38
0
  return self->ic_type;
39
0
}
40
41
guint16
42
fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self)
43
0
{
44
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
45
0
  return self->iap_addr;
46
0
}
47
48
gboolean
49
fu_elantp_firmware_get_forcetable_support(FuElantpFirmware *self)
50
0
{
51
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), FALSE);
52
0
  return self->force_table_support;
53
0
}
54
55
guint32
56
fu_elantp_firmware_get_forcetable_addr(FuElantpFirmware *self)
57
0
{
58
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
59
0
  return self->force_table_addr;
60
0
}
61
62
static void
63
fu_elantp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
64
0
{
65
0
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
66
0
  fu_xmlb_builder_insert_kx(bn, "ic_type", self->ic_type);
67
0
  fu_xmlb_builder_insert_kx(bn, "iap_addr", self->iap_addr);
68
0
  fu_xmlb_builder_insert_kx(bn, "module_id", self->module_id);
69
0
}
70
71
static gboolean
72
fu_elantp_firmware_validate(FuFirmware *firmware,
73
          GInputStream *stream,
74
          gsize offset,
75
          GError **error)
76
337
{
77
337
  gsize streamsz = 0;
78
79
337
  if (!fu_input_stream_size(stream, &streamsz, error))
80
0
    return FALSE;
81
337
  if (streamsz < FU_STRUCT_ELANTP_FIRMWARE_FTR_SIZE) {
82
6
    g_set_error_literal(error,
83
6
            FWUPD_ERROR,
84
6
            FWUPD_ERROR_INVALID_FILE,
85
6
            "stream was too small");
86
6
    return FALSE;
87
6
  }
88
331
  if (!fu_struct_elantp_firmware_ftr_validate_stream(stream,
89
331
                 streamsz -
90
331
                     FU_STRUCT_ELANTP_FIRMWARE_FTR_SIZE,
91
331
                 error))
92
58
    return FALSE;
93
94
  /* success */
95
273
  return TRUE;
96
331
}
97
98
static gboolean
99
fu_elantp_firmware_parse(FuFirmware *firmware,
100
       GInputStream *stream,
101
       FuFirmwareParseFlags flags,
102
       GError **error)
103
273
{
104
273
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
105
273
  guint16 force_table_addr_wrds;
106
273
  guint16 module_id_wrds;
107
273
  guint32 iap_addr_tmp;
108
273
  g_autoptr(FuStructElantpFirmwareHdr) st = NULL;
109
110
273
  st = fu_struct_elantp_firmware_hdr_parse_stream(stream,
111
273
              FU_ELANTP_FIRMWARE_OFFSET_IAP,
112
273
              error);
113
273
  if (st == NULL)
114
27
    return FALSE;
115
116
  /* convert from words */
117
246
  iap_addr_tmp = (guint32)fu_struct_elantp_firmware_hdr_get_iap_start(st) * 2;
118
246
  if (iap_addr_tmp >= G_MAXUINT16 ||
119
236
      iap_addr_tmp < (FU_ELANTP_FIRMWARE_OFFSET_IAP + FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE)) {
120
20
    g_set_error(error,
121
20
          FWUPD_ERROR,
122
20
          FWUPD_ERROR_INVALID_FILE,
123
20
          "IAP address invalid: 0x%x",
124
20
          iap_addr_tmp);
125
20
    return FALSE;
126
20
  }
127
226
  self->iap_addr = (guint16)iap_addr_tmp;
128
226
  self->ic_type = fu_struct_elantp_firmware_hdr_get_ic_type(st);
129
226
  self->iap_ver = fu_struct_elantp_firmware_hdr_get_iap_ver(st);
130
131
  /* read module ID */
132
226
  if (!fu_input_stream_read_u16(stream,
133
226
              self->iap_addr,
134
226
              &module_id_wrds,
135
226
              G_LITTLE_ENDIAN,
136
226
              error))
137
20
    return FALSE;
138
206
  if (module_id_wrds == 0xFFFF) {
139
1
    g_set_error(error,
140
1
          FWUPD_ERROR,
141
1
          FWUPD_ERROR_INVALID_FILE,
142
1
          "module ID address invalid: 0x%x",
143
1
          module_id_wrds);
144
1
    return FALSE;
145
1
  }
146
205
  if (!fu_input_stream_read_u16(stream,
147
205
              module_id_wrds * 2,
148
205
              &self->module_id,
149
205
              G_LITTLE_ENDIAN,
150
205
              error))
151
23
    return FALSE;
152
153
  /* get the forcetable address */
154
182
  if (self->ic_type == FU_ETP_IC_NUM12 || self->ic_type == FU_ETP_IC_NUM13 ||
155
151
      self->ic_type == FU_ETP_IC_NUM14 || self->ic_type == FU_ETP_IC_NUM15) {
156
45
    if (self->iap_ver <= 4) {
157
7
      if (!fu_input_stream_read_u16(stream,
158
7
                  self->iap_addr + 6,
159
7
                  &force_table_addr_wrds,
160
7
                  G_LITTLE_ENDIAN,
161
7
                  error))
162
1
        return FALSE;
163
38
    } else {
164
38
      force_table_addr_wrds =
165
38
          fu_struct_elantp_firmware_hdr_get_iap_forcetable(st);
166
38
    }
167
44
    if (force_table_addr_wrds == 0xFFFF) {
168
3
      self->force_table_support = FALSE;
169
41
    } else {
170
41
      if (force_table_addr_wrds % 32 != 0 || force_table_addr_wrds >= 0x7FFF) {
171
24
        g_set_error(error,
172
24
              FWUPD_ERROR,
173
24
              FWUPD_ERROR_INVALID_FILE,
174
24
              "forcetable address invalid: 0x%x",
175
24
              force_table_addr_wrds);
176
24
        return FALSE;
177
24
      }
178
17
      self->force_table_addr = force_table_addr_wrds * 2;
179
17
      self->force_table_support = TRUE;
180
17
    }
181
44
  }
182
183
  /* success */
184
157
  return TRUE;
185
182
}
186
187
static gboolean
188
fu_elantp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
189
0
{
190
0
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
191
0
  guint64 tmp;
192
193
  /* optional properties */
194
0
  tmp = xb_node_query_text_as_uint(n, "ic_type", NULL);
195
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
196
0
    self->ic_type = tmp;
197
0
  tmp = xb_node_query_text_as_uint(n, "module_id", NULL);
198
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
199
0
    self->module_id = tmp;
200
0
  tmp = xb_node_query_text_as_uint(n, "iap_addr", NULL);
201
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
202
0
    self->iap_addr = tmp;
203
204
  /* success */
205
0
  return TRUE;
206
0
}
207
208
static GByteArray *
209
fu_elantp_firmware_write(FuFirmware *firmware, GError **error)
210
157
{
211
157
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
212
157
  g_autoptr(GByteArray) buf = g_byte_array_new();
213
157
  g_autoptr(GBytes) blob = NULL;
214
157
  g_autoptr(FuStructElantpFirmwareHdr) st_hdr = fu_struct_elantp_firmware_hdr_new();
215
157
  g_autoptr(FuStructElantpFirmwareFtr) st_ftr = fu_struct_elantp_firmware_ftr_new();
216
217
  /* sanity check */
218
157
  if (self->iap_addr < FU_ELANTP_FIRMWARE_OFFSET_IAP + FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE) {
219
0
    g_set_error(error,
220
0
          FWUPD_ERROR,
221
0
          FWUPD_ERROR_INVALID_DATA,
222
0
          "IAP address 0x%x would truncate header",
223
0
          self->iap_addr);
224
0
    return NULL;
225
0
  }
226
227
  /* header */
228
157
  fu_byte_array_set_size(buf, FU_ELANTP_FIRMWARE_OFFSET_IAP, 0x00);
229
157
  fu_struct_elantp_firmware_hdr_set_ic_type(st_hdr, self->ic_type);
230
157
  fu_struct_elantp_firmware_hdr_set_iap_ver(st_hdr, self->iap_ver);
231
157
  fu_struct_elantp_firmware_hdr_set_iap_start(st_hdr, self->iap_addr / 2);
232
157
  fu_struct_elantp_firmware_hdr_set_iap_forcetable(
233
157
      st_hdr,
234
157
      self->force_table_support ? self->force_table_addr / 2 : 0xFFFF);
235
157
  g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len);
236
237
  /* IAP */
238
157
  fu_byte_array_set_size(buf, self->iap_addr + 0x4, 0x00);
239
157
  if (!fu_memwrite_uint16_safe(buf->data,
240
157
             buf->len,
241
157
             self->iap_addr,
242
157
             (self->iap_addr + 2) / 2,
243
157
             G_LITTLE_ENDIAN,
244
157
             error))
245
0
    return NULL;
246
157
  if (!fu_memwrite_uint16_safe(buf->data,
247
157
             buf->len,
248
157
             self->iap_addr + 0x2,
249
157
             self->module_id,
250
157
             G_LITTLE_ENDIAN,
251
157
             error))
252
0
    return NULL;
253
254
  /* data */
255
157
  blob = fu_firmware_get_bytes_with_patches(firmware, error);
256
157
  if (blob == NULL)
257
157
    return NULL;
258
0
  fu_byte_array_append_bytes(buf, blob);
259
260
  /* footer */
261
0
  g_byte_array_append(buf, st_ftr->buf->data, st_ftr->buf->len);
262
0
  return g_steal_pointer(&buf);
263
157
}
264
265
static void
266
fu_elantp_firmware_init(FuElantpFirmware *self)
267
337
{
268
337
}
269
270
static void
271
fu_elantp_firmware_class_init(FuElantpFirmwareClass *klass)
272
1
{
273
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
274
1
  firmware_class->validate = fu_elantp_firmware_validate;
275
1
  firmware_class->parse = fu_elantp_firmware_parse;
276
1
  firmware_class->build = fu_elantp_firmware_build;
277
1
  firmware_class->write = fu_elantp_firmware_write;
278
1
  firmware_class->export = fu_elantp_firmware_export;
279
1
}