Coverage Report

Created: 2025-12-14 06:56

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
206k
G_DEFINE_TYPE(FuElantpFirmware, fu_elantp_firmware, FU_TYPE_FIRMWARE)
24
206k
25
206k
/* firmware block update */
26
206k
#define ETP_IC_TYPE_ADDR_WRDS    0x0080
27
98
#define ETP_IAP_VER_ADDR_WRDS    0x0082
28
481
#define ETP_IAP_START_ADDR_WRDS    0x0083
29
14
#define ETP_IAP_FORCETABLE_ADDR_V5 0x0085
30
31
const guint8 elantp_signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF};
32
33
guint16
34
fu_elantp_firmware_get_module_id(FuElantpFirmware *self)
35
0
{
36
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
37
0
  return self->module_id;
38
0
}
39
40
guint16
41
fu_elantp_firmware_get_ic_type(FuElantpFirmware *self)
42
0
{
43
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
44
0
  return self->ic_type;
45
0
}
46
47
guint16
48
fu_elantp_firmware_get_iap_addr(FuElantpFirmware *self)
49
0
{
50
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
51
0
  return self->iap_addr;
52
0
}
53
54
gboolean
55
fu_elantp_firmware_get_forcetable_support(FuElantpFirmware *self)
56
0
{
57
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), FALSE);
58
0
  return self->force_table_support;
59
0
}
60
61
guint32
62
fu_elantp_firmware_get_forcetable_addr(FuElantpFirmware *self)
63
0
{
64
0
  g_return_val_if_fail(FU_IS_ELANTP_FIRMWARE(self), 0);
65
0
  return self->force_table_addr;
66
0
}
67
68
static void
69
fu_elantp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
70
0
{
71
0
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
72
0
  fu_xmlb_builder_insert_kx(bn, "iap_addr", self->iap_addr);
73
0
  fu_xmlb_builder_insert_kx(bn, "module_id", self->module_id);
74
0
}
75
76
static gboolean
77
fu_elantp_firmware_validate(FuFirmware *firmware,
78
          GInputStream *stream,
79
          gsize offset,
80
          GError **error)
81
205k
{
82
205k
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
83
205k
  gsize streamsz = 0;
84
85
205k
  if (!fu_input_stream_size(stream, &streamsz, error))
86
0
    return FALSE;
87
205k
  if (streamsz < FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE) {
88
32
    g_set_error_literal(error,
89
32
            FWUPD_ERROR,
90
32
            FWUPD_ERROR_INVALID_FILE,
91
32
            "stream was too small");
92
32
    return FALSE;
93
32
  }
94
205k
  if (!fu_struct_elantp_firmware_hdr_validate_stream(stream,
95
205k
                 streamsz -
96
205k
                     FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE,
97
205k
                 error))
98
205k
    return FALSE;
99
177
  if (self->force_table_addr != 0) {
100
0
    if (!fu_struct_elantp_firmware_hdr_validate_stream(
101
0
      stream,
102
0
      self->force_table_addr - 1 + FU_STRUCT_ELANTP_FIRMWARE_HDR_SIZE,
103
0
      error))
104
0
      return FALSE;
105
0
  }
106
107
  /* success */
108
177
  return TRUE;
109
177
}
110
111
static gboolean
112
fu_elantp_firmware_parse(FuFirmware *firmware,
113
       GInputStream *stream,
114
       FuFirmwareParseFlags flags,
115
       GError **error)
116
177
{
117
177
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
118
177
  guint16 iap_addr_wrds;
119
177
  guint16 force_table_addr_wrds;
120
177
  guint16 module_id_wrds;
121
177
  g_autoptr(GError) error_local = NULL;
122
123
  /* presumably in words */
124
177
  if (!fu_input_stream_read_u16(stream,
125
177
              ETP_IAP_START_ADDR_WRDS * 2,
126
177
              &iap_addr_wrds,
127
177
              G_LITTLE_ENDIAN,
128
177
              error))
129
25
    return FALSE;
130
152
  if (iap_addr_wrds < ETP_IAP_START_ADDR_WRDS || iap_addr_wrds > 0x7FFF) {
131
21
    g_set_error(error,
132
21
          FWUPD_ERROR,
133
21
          FWUPD_ERROR_INVALID_FILE,
134
21
          "IAP address invalid: 0x%x",
135
21
          iap_addr_wrds);
136
21
    return FALSE;
137
21
  }
138
131
  self->iap_addr = iap_addr_wrds * 2;
139
140
  /* read module ID */
141
131
  if (!fu_input_stream_read_u16(stream,
142
131
              self->iap_addr,
143
131
              &module_id_wrds,
144
131
              G_LITTLE_ENDIAN,
145
131
              error))
146
16
    return FALSE;
147
115
  if (module_id_wrds > 0x7FFF) {
148
12
    g_set_error(error,
149
12
          FWUPD_ERROR,
150
12
          FWUPD_ERROR_INVALID_FILE,
151
12
          "module ID address invalid: 0x%x",
152
12
          module_id_wrds);
153
12
    return FALSE;
154
12
  }
155
103
  if (!fu_input_stream_read_u16(stream,
156
103
              module_id_wrds * 2,
157
103
              &self->module_id,
158
103
              G_LITTLE_ENDIAN,
159
103
              error))
160
5
    return FALSE;
161
98
  if (!fu_input_stream_read_u16(stream,
162
98
              ETP_IC_TYPE_ADDR_WRDS * 2,
163
98
              &self->ic_type,
164
98
              G_LITTLE_ENDIAN,
165
98
              error))
166
0
    return FALSE;
167
98
  if (!fu_input_stream_read_u16(stream,
168
98
              ETP_IAP_VER_ADDR_WRDS * 2,
169
98
              &self->iap_ver,
170
98
              G_LITTLE_ENDIAN,
171
98
              error))
172
0
    return FALSE;
173
174
98
  if (self->ic_type != 0x12 && self->ic_type != 0x13)
175
78
    return TRUE;
176
177
20
  if (self->iap_ver <= 4) {
178
6
    if (!fu_input_stream_read_u16(stream,
179
6
                self->iap_addr + 6,
180
6
                &force_table_addr_wrds,
181
6
                G_LITTLE_ENDIAN,
182
6
                &error_local)) {
183
2
      g_debug("forcetable address wrong: %s", error_local->message);
184
2
      return TRUE;
185
2
    }
186
14
  } else {
187
14
    if (!fu_input_stream_read_u16(stream,
188
14
                ETP_IAP_FORCETABLE_ADDR_V5 * 2,
189
14
                &force_table_addr_wrds,
190
14
                G_LITTLE_ENDIAN,
191
14
                &error_local)) {
192
0
      g_debug("forcetable address wrong: %s", error_local->message);
193
0
      return TRUE;
194
0
    }
195
14
  }
196
197
18
  if (force_table_addr_wrds % 32 == 0) {
198
1
    self->force_table_addr = force_table_addr_wrds * 2;
199
1
    self->force_table_support = TRUE;
200
1
  }
201
202
  /* success */
203
18
  return TRUE;
204
20
}
205
206
static gboolean
207
fu_elantp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
208
0
{
209
0
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
210
0
  guint64 tmp;
211
212
  /* two simple properties */
213
0
  tmp = xb_node_query_text_as_uint(n, "module_id", NULL);
214
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
215
0
    self->module_id = tmp;
216
0
  tmp = xb_node_query_text_as_uint(n, "iap_addr", NULL);
217
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
218
0
    self->iap_addr = tmp;
219
220
  /* success */
221
0
  return TRUE;
222
0
}
223
224
static GByteArray *
225
fu_elantp_firmware_write(FuFirmware *firmware, GError **error)
226
98
{
227
98
  FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware);
228
98
  g_autoptr(GByteArray) buf = g_byte_array_new();
229
98
  g_autoptr(GBytes) blob = NULL;
230
231
  /* only one image supported */
232
98
  blob = fu_firmware_get_bytes_with_patches(firmware, error);
233
98
  if (blob == NULL)
234
98
    return NULL;
235
236
  /* lets build a simple firmware like this:
237
   * ------ 0x0
238
   * HEADER (containing IAP offset and module ID)
239
   * ------ ~0x10a
240
   *  DATA
241
   * ------
242
   *  SIGNATURE
243
   * ------
244
   */
245
0
  fu_byte_array_set_size(buf, self->iap_addr + 0x2 + 0x2, 0x00);
246
0
  if (!fu_memwrite_uint16_safe(buf->data,
247
0
             buf->len,
248
0
             ETP_IAP_START_ADDR_WRDS * 2,
249
0
             self->iap_addr / 2,
250
0
             G_LITTLE_ENDIAN,
251
0
             error))
252
0
    return NULL;
253
0
  if (!fu_memwrite_uint16_safe(buf->data,
254
0
             buf->len,
255
0
             self->iap_addr,
256
0
             (self->iap_addr + 2) / 2,
257
0
             G_LITTLE_ENDIAN,
258
0
             error))
259
0
    return NULL;
260
0
  if (!fu_memwrite_uint16_safe(buf->data,
261
0
             buf->len,
262
0
             self->iap_addr + 0x2,
263
0
             self->module_id,
264
0
             G_LITTLE_ENDIAN,
265
0
             error))
266
0
    return NULL;
267
0
  fu_byte_array_append_bytes(buf, blob);
268
0
  g_byte_array_append(buf, elantp_signature, sizeof(elantp_signature));
269
0
  return g_steal_pointer(&buf);
270
0
}
271
272
static void
273
fu_elantp_firmware_init(FuElantpFirmware *self)
274
301
{
275
301
}
276
277
static void
278
fu_elantp_firmware_class_init(FuElantpFirmwareClass *klass)
279
1
{
280
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
281
1
  firmware_class->validate = fu_elantp_firmware_validate;
282
1
  firmware_class->parse = fu_elantp_firmware_parse;
283
1
  firmware_class->build = fu_elantp_firmware_build;
284
1
  firmware_class->write = fu_elantp_firmware_write;
285
1
  firmware_class->export = fu_elantp_firmware_export;
286
1
}
287
288
FuFirmware *
289
fu_elantp_firmware_new(void)
290
0
{
291
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_ELANTP_FIRMWARE, NULL));
292
0
}