Coverage Report

Created: 2026-10-28 07:47

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
375
G_DEFINE_TYPE(FuElantpFirmware, fu_elantp_firmware, FU_TYPE_FIRMWARE)
24
375
25
953
#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
          FuInputStream *stream,
74
          gsize offset,
75
          GError **error)
76
373
{
77
373
  gsize streamsz = 0;
78
79
373
  if (!fu_input_stream_size(stream, &streamsz, error))
80
0
    return FALSE;
81
373
  if (streamsz < FU_STRUCT_ELANTP_FIRMWARE_FTR_SIZE) {
82
7
    g_set_error_literal(error,
83
7
            FWUPD_ERROR,
84
7
            FWUPD_ERROR_INVALID_FILE,
85
7
            "stream was too small");
86
7
    return FALSE;
87
7
  }
88
366
  if (!fu_struct_elantp_firmware_ftr_validate_stream(stream,
89
366
                 streamsz -
90
366
                     FU_STRUCT_ELANTP_FIRMWARE_FTR_SIZE,
91
366
                 error))
92
62
    return FALSE;
93
94
  /* success */
95
304
  return TRUE;
96
366
}
97
98
static gboolean
99
fu_elantp_firmware_parse(FuFirmware *firmware,
100
       FuInputStream *stream,
101
       FuFirmwareParseFlags flags,
102
       GError **error)
103
304
{
104
304
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
105
304
  guint16 force_table_addr_wrds;
106
304
  guint16 module_id_wrds;
107
304
  guint32 iap_addr_tmp;
108
304
  g_autoptr(FuStructElantpFirmwareHdr) st = NULL;
109
110
304
  st = fu_struct_elantp_firmware_hdr_parse_stream(stream,
111
304
              FU_ELANTP_FIRMWARE_OFFSET_IAP,
112
304
              error);
113
304
  if (st == NULL)
114
25
    return FALSE;
115
116
  /* convert from words */
117
279
  iap_addr_tmp = (guint32)fu_struct_elantp_firmware_hdr_get_iap_start(st) * 2;
118
279
  if (iap_addr_tmp >= G_MAXUINT16 ||
119
267
      iap_addr_tmp < (FU_ELANTP_FIRMWARE_OFFSET_IAP + FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE)) {
120
21
    g_set_error(error,
121
21
          FWUPD_ERROR,
122
21
          FWUPD_ERROR_INVALID_FILE,
123
21
          "IAP address invalid: 0x%x",
124
21
          iap_addr_tmp);
125
21
    return FALSE;
126
21
  }
127
258
  self->iap_addr = (guint16)iap_addr_tmp;
128
258
  self->ic_type = fu_struct_elantp_firmware_hdr_get_ic_type(st);
129
258
  self->iap_ver = fu_struct_elantp_firmware_hdr_get_iap_ver(st);
130
131
  /* read module ID */
132
258
  if (!fu_input_stream_read_u16(stream,
133
258
              self->iap_addr,
134
258
              &module_id_wrds,
135
258
              G_LITTLE_ENDIAN,
136
258
              error))
137
38
    return FALSE;
138
220
  if (module_id_wrds == 0xFFFF) {
139
3
    g_set_error(error,
140
3
          FWUPD_ERROR,
141
3
          FWUPD_ERROR_INVALID_FILE,
142
3
          "module ID address invalid: 0x%x",
143
3
          module_id_wrds);
144
3
    return FALSE;
145
3
  }
146
217
  if (!fu_input_stream_read_u16(stream,
147
217
              module_id_wrds * 2,
148
217
              &self->module_id,
149
217
              G_LITTLE_ENDIAN,
150
217
              error))
151
25
    return FALSE;
152
153
  /* get the forcetable address */
154
192
  if (self->ic_type == FU_ETP_IC_NUM12 || self->ic_type == FU_ETP_IC_NUM13 ||
155
164
      self->ic_type == FU_ETP_IC_NUM14 || self->ic_type == FU_ETP_IC_NUM15) {
156
44
    if (self->iap_ver <= 4) {
157
8
      if (!fu_input_stream_read_u16(stream,
158
8
                  self->iap_addr + 6,
159
8
                  &force_table_addr_wrds,
160
8
                  G_LITTLE_ENDIAN,
161
8
                  error))
162
1
        return FALSE;
163
36
    } else {
164
36
      force_table_addr_wrds =
165
36
          fu_struct_elantp_firmware_hdr_get_iap_forcetable(st);
166
36
    }
167
43
    if (force_table_addr_wrds == 0xFFFF || force_table_addr_wrds % 32 != 0 ||
168
27
        force_table_addr_wrds >= 0x7FFF) {
169
27
      self->force_table_support = FALSE;
170
27
    } else {
171
16
      self->force_table_addr = force_table_addr_wrds * 2;
172
16
      self->force_table_support = TRUE;
173
16
    }
174
43
  }
175
176
  /* success */
177
191
  return TRUE;
178
192
}
179
180
static gboolean
181
fu_elantp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
182
0
{
183
0
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
184
0
  guint64 tmp;
185
186
  /* optional properties */
187
0
  tmp = xb_node_query_text_as_uint(n, "ic_type", NULL);
188
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
189
0
    self->ic_type = tmp;
190
0
  tmp = xb_node_query_text_as_uint(n, "module_id", NULL);
191
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
192
0
    self->module_id = tmp;
193
0
  tmp = xb_node_query_text_as_uint(n, "iap_addr", NULL);
194
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
195
0
    self->iap_addr = tmp;
196
197
  /* success */
198
0
  return TRUE;
199
0
}
200
201
static GByteArray *
202
fu_elantp_firmware_write(FuFirmware *firmware, GError **error)
203
191
{
204
191
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
205
191
  g_autoptr(GByteArray) buf = g_byte_array_new();
206
191
  g_autoptr(GBytes) blob = NULL;
207
191
  g_autoptr(FuStructElantpFirmwareHdr) st_hdr = fu_struct_elantp_firmware_hdr_new();
208
191
  g_autoptr(FuStructElantpFirmwareFtr) st_ftr = fu_struct_elantp_firmware_ftr_new();
209
210
  /* sanity check */
211
191
  if (self->iap_addr < FU_ELANTP_FIRMWARE_OFFSET_IAP + FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE) {
212
0
    g_set_error(error,
213
0
          FWUPD_ERROR,
214
0
          FWUPD_ERROR_INVALID_DATA,
215
0
          "IAP address 0x%x would truncate header",
216
0
          self->iap_addr);
217
0
    return NULL;
218
0
  }
219
220
  /* header */
221
191
  fu_byte_array_set_size(buf, FU_ELANTP_FIRMWARE_OFFSET_IAP, 0x00);
222
191
  fu_struct_elantp_firmware_hdr_set_ic_type(st_hdr, self->ic_type);
223
191
  fu_struct_elantp_firmware_hdr_set_iap_ver(st_hdr, self->iap_ver);
224
191
  fu_struct_elantp_firmware_hdr_set_iap_start(st_hdr, self->iap_addr / 2);
225
191
  fu_struct_elantp_firmware_hdr_set_iap_forcetable(
226
191
      st_hdr,
227
191
      self->force_table_support ? self->force_table_addr / 2 : 0xFFFF);
228
191
  g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len);
229
230
  /* IAP */
231
191
  fu_byte_array_set_size(buf, self->iap_addr + 0x4, 0x00);
232
191
  if (!fu_memwrite_uint16_safe(buf->data,
233
191
             buf->len,
234
191
             self->iap_addr,
235
191
             (self->iap_addr + 2) / 2,
236
191
             G_LITTLE_ENDIAN,
237
191
             error))
238
0
    return NULL;
239
191
  if (!fu_memwrite_uint16_safe(buf->data,
240
191
             buf->len,
241
191
             self->iap_addr + 0x2,
242
191
             self->module_id,
243
191
             G_LITTLE_ENDIAN,
244
191
             error))
245
0
    return NULL;
246
247
  /* data */
248
191
  blob = fu_firmware_get_bytes_with_patches(firmware, error);
249
191
  if (blob == NULL)
250
0
    return NULL;
251
191
  fu_byte_array_append_bytes(buf, blob);
252
253
  /* footer */
254
191
  g_byte_array_append(buf, st_ftr->buf->data, st_ftr->buf->len);
255
191
  return g_steal_pointer(&buf);
256
191
}
257
258
static void
259
fu_elantp_firmware_init(FuElantpFirmware *self)
260
373
{
261
373
}
262
263
static void
264
fu_elantp_firmware_class_init(FuElantpFirmwareClass *klass)
265
1
{
266
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
267
1
  firmware_class->validate = fu_elantp_firmware_validate;
268
1
  firmware_class->parse = fu_elantp_firmware_parse;
269
1
  firmware_class->build = fu_elantp_firmware_build;
270
1
  firmware_class->write = fu_elantp_firmware_write;
271
1
  firmware_class->export = fu_elantp_firmware_export;
272
1
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
273
1
}