Coverage Report

Created: 2026-10-17 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2012 Andrew Duggan
3
 * Copyright 2012 Synaptics Inc.
4
 * Copyright 2019 Richard Hughes <richard@hughsie.com>
5
 *
6
 * SPDX-License-Identifier: LGPL-2.1-or-later
7
 */
8
9
#include "config.h"
10
11
#include <string.h>
12
13
#include "fu-synaptics-rmi-common.h"
14
#include "fu-synaptics-rmi-firmware.h"
15
#include "fu-synaptics-rmi-struct.h"
16
17
struct _FuSynapticsRmiFirmware {
18
  FuFirmware parent_instance;
19
  FuSynapticsRmiFirmwareKind kind;
20
  guint32 checksum;
21
  guint8 io;
22
  guint8 bootloader_version;
23
  guint32 build_id;
24
  guint32 package_id;
25
  guint16 product_info;
26
  gchar *product_id;
27
  guint32 sig_size;
28
};
29
30
1.01k
G_DEFINE_TYPE(FuSynapticsRmiFirmware, fu_synaptics_rmi_firmware, FU_TYPE_FIRMWARE)
31
1.01k
32
1.01k
#define RMI_IMG_FW_OFFSET 0x100
33
34
743
#define RMI_IMG_V10_CNTR_ADDR_OFFSET 0x0c
35
2.86k
#define RMI_IMG_MAX_CONTAINERS       1024
36
37
static gboolean
38
fu_synaptics_rmi_firmware_parse_sbl_container_v10(FuSynapticsRmiFirmware *self,
39
              FuInputStream *stream,
40
              const guint8 *buf,
41
              gsize bufsz,
42
              guint32 start_offset,
43
              FuFirmwareParseFlags flags,
44
              GError **error);
45
46
static gboolean
47
fu_synaptics_rmi_firmware_add_image(FuSynapticsRmiFirmware *self,
48
            const gchar *id,
49
            FuInputStream *stream,
50
            gsize offset,
51
            gsize bufsz,
52
            FuFirmwareParseFlags flags,
53
            GError **error)
54
14.3k
{
55
14.3k
  g_autoptr(FuFirmware) img = fu_firmware_new();
56
14.3k
  g_autoptr(FuInputStream) partial_stream = NULL;
57
14.3k
  partial_stream = fu_partial_input_stream_new(stream, offset, bufsz, error);
58
14.3k
  if (partial_stream == NULL)
59
144
    return FALSE;
60
14.2k
  if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
61
0
    return FALSE;
62
14.2k
  fu_firmware_set_id(img, id);
63
14.2k
  return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
64
14.2k
}
65
66
static gboolean
67
fu_synaptics_rmi_firmware_add_image_v10(FuSynapticsRmiFirmware *self,
68
          const gchar *id,
69
          FuInputStream *stream,
70
          gsize offset,
71
          gsize bufsz,
72
          gsize sig_sz,
73
          FuFirmwareParseFlags flags,
74
          GError **error)
75
14.1k
{
76
14.1k
  if (!fu_synaptics_rmi_firmware_add_image(self, id, stream, offset, bufsz, flags, error))
77
43
    return FALSE;
78
14.1k
  if (sig_sz != 0) {
79
12.0k
    g_autoptr(FuInputStream) partial_stream = NULL;
80
12.0k
    g_autoptr(FuFirmware) img = fu_firmware_new();
81
12.0k
    g_autofree gchar *sig_id = NULL;
82
83
12.0k
    partial_stream = fu_partial_input_stream_new(stream, offset + bufsz, sig_sz, error);
84
12.0k
    if (partial_stream == NULL)
85
63
      return FALSE;
86
11.9k
    if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
87
0
      return FALSE;
88
11.9k
    sig_id = g_strdup_printf("%s-signature", id);
89
11.9k
    fu_firmware_set_id(img, sig_id);
90
11.9k
    if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error))
91
1
      return FALSE;
92
11.9k
  }
93
14.0k
  return TRUE;
94
14.1k
}
95
96
static void
97
fu_synaptics_rmi_firmware_export(FuFirmware *firmware,
98
         FuFirmwareExportFlags flags,
99
         XbBuilderNode *bn)
100
0
{
101
0
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
102
0
  fu_xmlb_builder_insert_kx(bn, "kind", self->kind);
103
0
  fu_xmlb_builder_insert_kv(bn, "product_id", self->product_id);
104
0
  if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) {
105
0
    fu_xmlb_builder_insert_kx(bn, "bootloader_version", self->bootloader_version);
106
0
    fu_xmlb_builder_insert_kx(bn, "io", self->io);
107
0
    fu_xmlb_builder_insert_kx(bn, "checksum", self->checksum);
108
0
    fu_xmlb_builder_insert_kx(bn, "build_id", self->build_id);
109
0
    fu_xmlb_builder_insert_kx(bn, "package_id", self->package_id);
110
0
    fu_xmlb_builder_insert_kx(bn, "product_info", self->product_info);
111
0
    fu_xmlb_builder_insert_kx(bn, "sig_size", self->sig_size);
112
0
  }
113
0
}
114
115
static gboolean
116
fu_synaptics_rmi_firmware_parse_v10(FuSynapticsRmiFirmware *self,
117
            FuInputStream *stream,
118
            FuFirmwareParseFlags flags,
119
            GError **error)
120
734
{
121
734
  guint16 container_id;
122
734
  guint32 cntrs_len;
123
734
  guint32 offset;
124
734
  guint32 cntr_addr;
125
734
  guint8 product_id[RMI_PRODUCT_ID_LENGTH] = {0x0};
126
734
  gsize bufsz = 0;
127
734
  const guint8 *buf;
128
734
  guint32 signature_size;
129
734
  g_autoptr(FuStructRmiContainerDescriptor) st_dsc = NULL;
130
734
  g_autoptr(GBytes) fw = NULL;
131
132
  /* maybe stream later */
133
734
  fw = fu_input_stream_read_bytes(stream, 0, G_MAXSIZE, NULL, error);
134
734
  if (fw == NULL)
135
0
    return FALSE;
136
734
  buf = g_bytes_get_data(fw, &bufsz);
137
138
734
  if (!fu_memread_uint32_safe(buf,
139
734
            bufsz,
140
734
            RMI_IMG_V10_CNTR_ADDR_OFFSET,
141
734
            &cntr_addr,
142
734
            G_LITTLE_ENDIAN,
143
734
            error))
144
0
    return FALSE;
145
734
  g_debug("v10 RmiContainerDescriptor at 0x%x", cntr_addr);
146
734
  st_dsc = fu_struct_rmi_container_descriptor_parse_stream(stream, cntr_addr, error);
147
734
  if (st_dsc == NULL) {
148
4
    g_prefix_error_literal(error, "RmiContainerDescriptor invalid: ");
149
4
    return FALSE;
150
4
  }
151
730
  if (bufsz < sizeof(guint32) + st_dsc->buf->len) {
152
0
    g_set_error_literal(error,
153
0
            FWUPD_ERROR,
154
0
            FWUPD_ERROR_INVALID_FILE,
155
0
            "stream was too small");
156
0
    return FALSE;
157
0
  }
158
730
  container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc);
159
730
  if (container_id != FU_RMI_CONTAINER_ID_TOP_LEVEL) {
160
15
    g_set_error(error,
161
15
          FWUPD_ERROR,
162
15
          FWUPD_ERROR_INVALID_FILE,
163
15
          "toplevel container_id invalid, got 0x%x expected 0x%x",
164
15
          (guint)container_id,
165
15
          (guint)FU_RMI_CONTAINER_ID_TOP_LEVEL);
166
15
    return FALSE;
167
15
  }
168
715
  offset = fu_struct_rmi_container_descriptor_get_content_address(st_dsc);
169
715
  if (offset > bufsz - sizeof(guint32) - st_dsc->buf->len) {
170
21
    g_set_error(error,
171
21
          FWUPD_ERROR,
172
21
          FWUPD_ERROR_INVALID_FILE,
173
21
          "image offset invalid, got 0x%x, size 0x%x",
174
21
          (guint)offset,
175
21
          (guint)bufsz);
176
21
    return FALSE;
177
21
  }
178
694
  cntrs_len = fu_struct_rmi_container_descriptor_get_content_length(st_dsc) / 4;
179
694
  if (cntrs_len > RMI_IMG_MAX_CONTAINERS) {
180
30
    g_set_error(error,
181
30
          FWUPD_ERROR,
182
30
          FWUPD_ERROR_INVALID_FILE,
183
30
          "too many containers in file [%u], maximum is %u",
184
30
          cntrs_len,
185
30
          (guint)RMI_IMG_MAX_CONTAINERS);
186
30
    return FALSE;
187
30
  }
188
664
  g_debug("offset=0x%x (cntrs_len=%u)", offset, cntrs_len);
189
190
14.8k
  for (guint32 i = 0; i < cntrs_len; i++) {
191
14.7k
    guint32 content_addr;
192
14.7k
    guint32 addr;
193
14.7k
    guint32 length;
194
14.7k
    g_autoptr(FuStructRmiContainerDescriptor) st_dsc2 = NULL;
195
196
14.7k
    if (offset > G_MAXUINT32 - 4) {
197
0
      g_set_error(error,
198
0
            FWUPD_ERROR,
199
0
            FWUPD_ERROR_INVALID_FILE,
200
0
            "offset overflow at container %u",
201
0
            i);
202
0
      return FALSE;
203
0
    }
204
14.7k
    if (!fu_memread_uint32_safe(buf, bufsz, offset, &addr, G_LITTLE_ENDIAN, error))
205
147
      return FALSE;
206
14.6k
    g_debug("parsing RmiContainerDescriptor at 0x%x", addr);
207
208
14.6k
    st_dsc2 = fu_struct_rmi_container_descriptor_parse_stream(stream, addr, error);
209
14.6k
    if (st_dsc2 == NULL)
210
162
      return FALSE;
211
14.4k
    container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc2);
212
14.4k
    content_addr = fu_struct_rmi_container_descriptor_get_content_address(st_dsc2);
213
14.4k
    length = fu_struct_rmi_container_descriptor_get_content_length(st_dsc2);
214
14.4k
    signature_size = fu_struct_rmi_container_descriptor_get_signature_size(st_dsc2);
215
14.4k
    g_debug("RmiContainerDescriptor 0x%02x @ 0x%x (len 0x%x) sig_size 0x%x",
216
14.4k
      container_id,
217
14.4k
      content_addr,
218
14.4k
      length,
219
14.4k
      signature_size);
220
14.4k
    if (length == 0 || length > bufsz) {
221
88
      g_set_error(error,
222
88
            FWUPD_ERROR,
223
88
            FWUPD_ERROR_INVALID_FILE,
224
88
            "length invalid, length 0x%x, size 0x%x",
225
88
            (guint)length,
226
88
            (guint)bufsz);
227
88
      return FALSE;
228
88
    }
229
14.3k
    if (content_addr > bufsz - length) {
230
29
      g_set_error(error,
231
29
            FWUPD_ERROR,
232
29
            FWUPD_ERROR_INVALID_FILE,
233
29
            "address invalid, got 0x%x (length 0x%x), size 0x%x",
234
29
            (guint)content_addr,
235
29
            (guint)length,
236
29
            (guint)bufsz);
237
29
      return FALSE;
238
29
    }
239
14.3k
    switch (container_id) {
240
2.13k
    case FU_RMI_CONTAINER_ID_BL:
241
2.13k
      if (!fu_memread_uint8_safe(buf,
242
2.13k
               bufsz,
243
2.13k
               content_addr,
244
2.13k
               &self->bootloader_version,
245
2.13k
               error))
246
0
        return FALSE;
247
2.13k
      if (!fu_synaptics_rmi_firmware_parse_sbl_container_v10(self,
248
2.13k
                         stream,
249
2.13k
                         buf,
250
2.13k
                         bufsz,
251
2.13k
                         offset,
252
2.13k
                         flags,
253
2.13k
                         error))
254
138
        return FALSE;
255
1.99k
      break;
256
1.99k
    case FU_RMI_CONTAINER_ID_UI:
257
1.41k
    case FU_RMI_CONTAINER_ID_CORE_CODE:
258
1.41k
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
259
1.41k
                     "ui",
260
1.41k
                     stream,
261
1.41k
                     content_addr,
262
1.41k
                     length,
263
1.41k
                     signature_size,
264
1.41k
                     flags,
265
1.41k
                     error))
266
4
        return FALSE;
267
1.41k
      break;
268
1.41k
    case FU_RMI_CONTAINER_ID_FLASH_CONFIG:
269
604
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
270
604
                     "flash-config",
271
604
                     stream,
272
604
                     content_addr,
273
604
                     length,
274
604
                     signature_size,
275
604
                     flags,
276
604
                     error))
277
1
        return FALSE;
278
603
      break;
279
799
    case FU_RMI_CONTAINER_ID_UI_CONFIG:
280
998
    case FU_RMI_CONTAINER_ID_CORE_CONFIG:
281
998
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
282
998
                     "config",
283
998
                     stream,
284
998
                     content_addr,
285
998
                     length,
286
998
                     signature_size,
287
998
                     flags,
288
998
                     error))
289
4
        return FALSE;
290
994
      break;
291
994
    case FU_RMI_CONTAINER_ID_FIXED_LOCATION_DATA:
292
312
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
293
312
                     "fixed-location-data",
294
312
                     stream,
295
312
                     content_addr,
296
312
                     length,
297
312
                     signature_size,
298
312
                     flags,
299
312
                     error))
300
3
        return FALSE;
301
309
      break;
302
385
    case FU_RMI_CONTAINER_ID_EXTERNAL_TOUCH_AFE_CONFIG:
303
385
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
304
385
                     "afe-config",
305
385
                     stream,
306
385
                     content_addr,
307
385
                     length,
308
385
                     signature_size,
309
385
                     flags,
310
385
                     error))
311
6
        return FALSE;
312
379
      break;
313
1.84k
    case FU_RMI_CONTAINER_ID_DISPLAY_CONFIG:
314
1.84k
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
315
1.84k
                     "display-config",
316
1.84k
                     stream,
317
1.84k
                     content_addr,
318
1.84k
                     length,
319
1.84k
                     signature_size,
320
1.84k
                     flags,
321
1.84k
                     error))
322
5
        return FALSE;
323
1.84k
      break;
324
1.84k
    case FU_RMI_CONTAINER_ID_GENERAL_INFORMATION:
325
393
      if (length < 0x18 + RMI_PRODUCT_ID_LENGTH) {
326
5
        g_set_error(error,
327
5
              FWUPD_ERROR,
328
5
              FWUPD_ERROR_INVALID_FILE,
329
5
              "content_addr invalid, got 0x%x (length 0x%x)",
330
5
              content_addr,
331
5
              (guint)length);
332
5
        return FALSE;
333
5
      }
334
393
      g_clear_pointer(&self->product_id, g_free);
335
388
      self->io = 1;
336
388
      if (!fu_memread_uint32_safe(buf,
337
388
                bufsz,
338
388
                content_addr,
339
388
                &self->package_id,
340
388
                G_LITTLE_ENDIAN,
341
388
                error))
342
0
        return FALSE;
343
388
      if (!fu_memread_uint32_safe(buf,
344
388
                bufsz,
345
388
                content_addr + 0x04,
346
388
                &self->build_id,
347
388
                G_LITTLE_ENDIAN,
348
388
                error))
349
0
        return FALSE;
350
388
      if (!fu_memcpy_safe(product_id,
351
388
              sizeof(product_id),
352
388
              0x0, /* dst */
353
388
              buf,
354
388
              bufsz,
355
388
              content_addr + 0x18, /* src */
356
388
              sizeof(product_id),
357
388
              error))
358
0
        return FALSE;
359
388
      break;
360
6.26k
    default:
361
6.26k
      g_debug("unsupported container %s [0x%02x]",
362
6.26k
        fu_rmi_container_id_to_string(container_id),
363
6.26k
        container_id);
364
6.26k
      break;
365
14.3k
    }
366
14.1k
    offset += 4;
367
14.1k
  }
368
72
  if (product_id[0] != '\0') {
369
4
    g_free(self->product_id);
370
4
    self->product_id = g_strndup((const gchar *)product_id, sizeof(product_id));
371
4
  }
372
72
  return TRUE;
373
664
}
374
375
static gboolean
376
fu_synaptics_rmi_firmware_parse_v0x(FuSynapticsRmiFirmware *self,
377
            FuInputStream *stream,
378
            FuFirmwareParseFlags flags,
379
            GError **error)
380
205
{
381
205
  guint32 cfg_sz;
382
205
  guint32 img_sz;
383
205
  g_autoptr(FuStructRmiImg) st_img = NULL;
384
385
  /* main firmware */
386
205
  st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error);
387
205
  if (st_img == NULL)
388
0
    return FALSE;
389
205
  img_sz = fu_struct_rmi_img_get_image_size(st_img);
390
205
  if (img_sz > 0) {
391
    /* payload, then signature appended */
392
170
    if (self->sig_size > 0) {
393
84
      guint32 sig_offset;
394
84
      if (self->sig_size > img_sz) {
395
37
        g_set_error(error,
396
37
              FWUPD_ERROR,
397
37
              FWUPD_ERROR_INVALID_DATA,
398
37
              "signature size 0x%x exceeds image size 0x%x",
399
37
              self->sig_size,
400
37
              img_sz);
401
37
        return FALSE;
402
37
      }
403
47
      sig_offset = img_sz - self->sig_size;
404
47
      if (!fu_synaptics_rmi_firmware_add_image(self,
405
47
                 "sig",
406
47
                 stream,
407
47
                 RMI_IMG_FW_OFFSET + sig_offset,
408
47
                 self->sig_size,
409
47
                 flags,
410
47
                 error))
411
37
        return FALSE;
412
47
    }
413
96
    if (!fu_synaptics_rmi_firmware_add_image(self,
414
96
               "ui",
415
96
               stream,
416
96
               RMI_IMG_FW_OFFSET,
417
96
               img_sz,
418
96
               flags,
419
96
               error))
420
13
      return FALSE;
421
96
  }
422
423
  /* config */
424
118
  cfg_sz = fu_struct_rmi_img_get_config_size(st_img);
425
118
  if (cfg_sz > 0) {
426
66
    if ((gsize)RMI_IMG_FW_OFFSET + (gsize)img_sz > G_MAXUINT32) {
427
0
      g_set_error_literal(error,
428
0
              FWUPD_ERROR,
429
0
              FWUPD_ERROR_INVALID_DATA,
430
0
              "config offset would overflow");
431
0
      return FALSE;
432
0
    }
433
66
    if (!fu_synaptics_rmi_firmware_add_image(self,
434
66
               "config",
435
66
               stream,
436
66
               RMI_IMG_FW_OFFSET + img_sz,
437
66
               cfg_sz,
438
66
               flags,
439
66
               error))
440
52
      return FALSE;
441
66
  }
442
66
  return TRUE;
443
118
}
444
445
static gboolean
446
fu_synaptics_rmi_firmware_parse(FuFirmware *firmware,
447
        FuInputStream *stream,
448
        FuFirmwareParseFlags flags,
449
        GError **error)
450
1.01k
{
451
1.01k
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
452
1.01k
  gsize bufsz = 0;
453
1.01k
  const guint8 *buf;
454
1.01k
  g_autoptr(FuStructRmiImg) st_img = NULL;
455
1.01k
  g_autoptr(GBytes) fw = NULL;
456
457
  /* maybe stream later */
458
1.01k
  fw = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error);
459
1.01k
  if (fw == NULL)
460
0
    return FALSE;
461
1.01k
  buf = g_bytes_get_data(fw, &bufsz);
462
463
  /* sanity check */
464
1.01k
  st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error);
465
1.01k
  if (st_img == NULL)
466
30
    return FALSE;
467
986
  if (bufsz % 2 != 0) {
468
38
    g_set_error_literal(error,
469
38
            FWUPD_ERROR,
470
38
            FWUPD_ERROR_INVALID_FILE,
471
38
            "data not aligned to 16 bits");
472
38
    return FALSE;
473
38
  }
474
948
  if (bufsz < 4) {
475
0
    g_set_error_literal(error,
476
0
            FWUPD_ERROR,
477
0
            FWUPD_ERROR_INVALID_FILE,
478
0
            "stream was too small");
479
0
    return FALSE;
480
0
  }
481
482
  /* verify checksum */
483
948
  self->checksum = fu_struct_rmi_img_get_checksum(st_img);
484
948
  if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) {
485
0
    guint32 checksum_calculated =
486
0
        fu_synaptics_rmi_generate_checksum(buf + 4, bufsz - 4);
487
0
    if (self->checksum != checksum_calculated) {
488
0
      g_set_error(error,
489
0
            FWUPD_ERROR,
490
0
            FWUPD_ERROR_INVALID_FILE,
491
0
            "checksum verification failed, got 0x%08x, actual 0x%08x",
492
0
            (guint)self->checksum,
493
0
            (guint)checksum_calculated);
494
0
      return FALSE;
495
0
    }
496
0
  }
497
498
  /* parse legacy image */
499
948
  g_clear_pointer(&self->product_id, g_free);
500
948
  self->io = fu_struct_rmi_img_get_io_offset(st_img);
501
948
  self->bootloader_version = fu_struct_rmi_img_get_bootloader_version(st_img);
502
948
  if (self->io == 1) {
503
457
    self->build_id = fu_struct_rmi_img_get_fw_build_id(st_img);
504
457
    self->package_id = fu_struct_rmi_img_get_package_id(st_img);
505
457
  }
506
948
  self->product_id = fu_struct_rmi_img_get_product_id(st_img);
507
948
  self->product_info = fu_struct_rmi_img_get_product_info(st_img);
508
948
  fu_firmware_set_size(firmware, fu_struct_rmi_img_get_image_size(st_img));
509
510
  /* parse partitions, but ignore lockdown */
511
948
  switch (self->bootloader_version) {
512
24
  case 2:
513
58
  case 3:
514
108
  case 4:
515
170
  case 5:
516
205
  case 6:
517
205
    if ((self->io & 0x10) >> 1)
518
149
      self->sig_size = fu_struct_rmi_img_get_signature_size(st_img);
519
205
    if (!fu_synaptics_rmi_firmware_parse_v0x(self, stream, flags, error))
520
139
      return FALSE;
521
66
    self->kind = FU_SYNAPTICS_RMI_FIRMWARE_KIND_V0X;
522
66
    break;
523
681
  case 16:
524
734
  case 17:
525
734
    if (!fu_synaptics_rmi_firmware_parse_v10(self, stream, flags, error))
526
662
      return FALSE;
527
72
    self->kind = FU_SYNAPTICS_RMI_FIRMWARE_KIND_V10;
528
72
    break;
529
9
  default:
530
9
    g_set_error(error,
531
9
          FWUPD_ERROR,
532
9
          FWUPD_ERROR_INVALID_FILE,
533
9
          "unsupported image version 0x%02x",
534
9
          self->bootloader_version);
535
9
    return FALSE;
536
948
  }
537
538
  /* success */
539
138
  return TRUE;
540
948
}
541
542
guint32
543
fu_synaptics_rmi_firmware_get_sig_size(FuSynapticsRmiFirmware *self)
544
0
{
545
0
  return self->sig_size;
546
0
}
547
548
static GByteArray *
549
fu_synaptics_rmi_firmware_write_v0x(FuSynapticsRmiFirmware *self, GError **error)
550
66
{
551
66
  gsize bufsz = 0;
552
66
  guint32 csum;
553
66
  g_autoptr(FuFirmware) img = NULL;
554
66
  g_autoptr(GByteArray) buf = g_byte_array_new();
555
66
  g_autoptr(FuStructRmiImg) st_img = fu_struct_rmi_img_new();
556
66
  g_autoptr(GBytes) buf_blob = NULL;
557
558
  /* default image */
559
66
  img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), "ui", error);
560
66
  if (img == NULL)
561
1
    return NULL;
562
65
  buf_blob = fu_firmware_write(img, error);
563
65
  if (buf_blob == NULL)
564
0
    return NULL;
565
65
  bufsz = g_bytes_get_size(buf_blob);
566
567
  /* create empty block */
568
65
  fu_struct_rmi_img_set_bootloader_version(st_img, 0x2); /* not hierarchical */
569
65
  if (self->product_id != NULL) {
570
18
    if (!fu_struct_rmi_img_set_product_id(st_img, self->product_id, error))
571
0
      return NULL;
572
18
  }
573
65
  fu_struct_rmi_img_set_product_info(st_img, 0x1234);
574
65
  fu_struct_rmi_img_set_image_size(st_img, bufsz);
575
65
  fu_struct_rmi_img_set_config_size(st_img, bufsz);
576
65
  fu_byte_array_append_array(buf, st_img->buf);
577
65
  fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x4 + bufsz, 0x00);
578
65
  fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET, 0xDEAD, G_LITTLE_ENDIAN); /* img */
579
65
  fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + bufsz,
580
65
         0xBEEF,
581
65
         G_LITTLE_ENDIAN); /* config */
582
583
  /* fixup checksum */
584
65
  csum = fu_synaptics_rmi_generate_checksum(buf->data + 4, buf->len - 4);
585
65
  fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_CHECKSUM, csum, G_LITTLE_ENDIAN);
586
587
  /* success */
588
65
  return g_steal_pointer(&buf);
589
65
}
590
591
static GByteArray *
592
fu_synaptics_rmi_firmware_write_v10(FuSynapticsRmiFirmware *self, GError **error)
593
72
{
594
72
  gsize bufsz;
595
72
  guint32 csum;
596
72
  g_autoptr(FuFirmware) img = NULL;
597
72
  g_autoptr(GByteArray) buf = g_byte_array_new();
598
72
  g_autoptr(FuStructRmiContainerDescriptor) st_dsc2 =
599
72
      fu_struct_rmi_container_descriptor_new();
600
72
  g_autoptr(FuStructRmiContainerDescriptor) st_dsc = fu_struct_rmi_container_descriptor_new();
601
72
  g_autoptr(GBytes) buf_blob = NULL;
602
603
  /* header | desc_hdr | offset_table | desc | flash_config |
604
   *        \0x0       \0x20          \0x24  \0x44          |0x48 */
605
72
  guint32 offset_table[] = {/* offset to first descriptor */
606
72
          GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x24),
607
72
          /* nocheck:blocked */};
608
72
  fu_struct_rmi_container_descriptor_set_container_id(st_dsc,
609
72
                  FU_RMI_CONTAINER_ID_FLASH_CONFIG);
610
72
  fu_struct_rmi_container_descriptor_set_content_address(st_dsc, RMI_IMG_FW_OFFSET + 0x44);
611
612
  /* default image */
613
72
  img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), "ui", error);
614
72
  if (img == NULL)
615
63
    return NULL;
616
9
  buf_blob = fu_firmware_write(img, error);
617
9
  if (buf_blob == NULL)
618
0
    return NULL;
619
9
  bufsz = g_bytes_get_size(buf_blob);
620
9
  fu_struct_rmi_container_descriptor_set_content_length(st_dsc, bufsz);
621
622
  /* create empty block */
623
9
  fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x48, 0x00);
624
9
  buf->data[FU_STRUCT_RMI_IMG_OFFSET_IO_OFFSET] = 0x1;
625
9
  buf->data[FU_STRUCT_RMI_IMG_OFFSET_BOOTLOADER_VERSION] = 16; /* hierarchical */
626
9
  if (self->product_id != NULL) {
627
7
    gsize product_id_sz = strlen(self->product_id);
628
7
    if (!fu_memcpy_safe(buf->data,
629
7
            buf->len,
630
7
            FU_STRUCT_RMI_IMG_OFFSET_PRODUCT_ID, /* dst */
631
7
            (const guint8 *)self->product_id,
632
7
            product_id_sz,
633
7
            0x0, /* src */
634
7
            product_id_sz,
635
7
            error))
636
0
      return NULL;
637
7
  }
638
  /* nocheck:memread */
639
9
  fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_FW_BUILD_ID,
640
9
         0x1234,
641
9
         G_LITTLE_ENDIAN);
642
9
  fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_PACKAGE_ID,
643
9
         0x4321,
644
9
         G_LITTLE_ENDIAN);
645
9
  fu_memwrite_uint16(buf->data + FU_STRUCT_RMI_IMG_OFFSET_PRODUCT_INFO,
646
9
         0x3456,
647
9
         G_LITTLE_ENDIAN);
648
9
  fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_IMAGE_SIZE, bufsz, G_LITTLE_ENDIAN);
649
9
  fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_CONFIG_SIZE,
650
9
         bufsz,
651
9
         G_LITTLE_ENDIAN);
652
9
  fu_memwrite_uint32(buf->data + RMI_IMG_V10_CNTR_ADDR_OFFSET,
653
9
         RMI_IMG_FW_OFFSET,
654
9
         G_LITTLE_ENDIAN);
655
656
  /* hierarchical section */
657
9
  fu_struct_rmi_container_descriptor_set_container_id(st_dsc2, FU_RMI_CONTAINER_ID_TOP_LEVEL);
658
9
  fu_struct_rmi_container_descriptor_set_content_length(st_dsc2, 0x1 * 4); /* bytes */
659
9
  fu_struct_rmi_container_descriptor_set_content_address(st_dsc2,
660
9
                     RMI_IMG_FW_OFFSET +
661
9
                   0x20); /* offset to table */
662
9
  memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x00,        /* nocheck:blocked */
663
9
         st_dsc2->buf->data,
664
9
         st_dsc2->buf->len);
665
9
  memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x20, /* nocheck:blocked */
666
9
         offset_table,
667
9
         sizeof(offset_table));
668
9
  memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x24, /* nocheck:blocked */
669
9
         st_dsc->buf->data,
670
9
         st_dsc->buf->len);
671
9
  fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + 0x44,
672
9
         0xfeed,
673
9
         G_LITTLE_ENDIAN); /* flash_config */
674
675
  /* fixup checksum */
676
9
  csum = fu_synaptics_rmi_generate_checksum(buf->data + 4, buf->len - 4);
677
9
  fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_CHECKSUM, csum, G_LITTLE_ENDIAN);
678
679
  /* success */
680
9
  return g_steal_pointer(&buf);
681
9
}
682
683
static gboolean
684
fu_synaptics_rmi_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
685
0
{
686
0
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
687
0
  const gchar *product_id;
688
0
  guint64 tmp;
689
690
  /* either 0x or 10 */
691
0
  tmp = xb_node_query_text_as_uint(n, "kind", NULL);
692
0
  if (tmp != G_MAXUINT64)
693
0
    self->kind = tmp;
694
695
  /* any string */
696
0
  product_id = xb_node_query_text(n, "product_id", NULL);
697
0
  if (product_id != NULL) {
698
0
    gsize product_id_sz = strlen(product_id);
699
0
    if (product_id_sz > RMI_PRODUCT_ID_LENGTH) {
700
0
      g_set_error(error,
701
0
            FWUPD_ERROR,
702
0
            FWUPD_ERROR_NOT_SUPPORTED,
703
0
            "product_id not supported, %u of %u bytes",
704
0
            (guint)product_id_sz,
705
0
            (guint)RMI_PRODUCT_ID_LENGTH);
706
0
      return FALSE;
707
0
    }
708
0
    g_set_str(&self->product_id, product_id);
709
0
  }
710
711
  /* success */
712
0
  return TRUE;
713
0
}
714
715
static GByteArray *
716
fu_synaptics_rmi_firmware_write(FuFirmware *firmware, GError **error)
717
138
{
718
138
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
719
720
  /* two supported container formats */
721
138
  if (self->kind == FU_SYNAPTICS_RMI_FIRMWARE_KIND_V0X)
722
66
    return fu_synaptics_rmi_firmware_write_v0x(self, error);
723
72
  if (self->kind == FU_SYNAPTICS_RMI_FIRMWARE_KIND_V10)
724
72
    return fu_synaptics_rmi_firmware_write_v10(self, error);
725
726
  /* not supported */
727
0
  g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "kind not supported");
728
0
  return NULL;
729
72
}
730
731
static void
732
fu_synaptics_rmi_firmware_init(FuSynapticsRmiFirmware *self)
733
1.01k
{
734
1.01k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
735
1.01k
}
736
737
static void
738
fu_synaptics_rmi_firmware_finalize(GObject *obj)
739
1.01k
{
740
1.01k
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(obj);
741
1.01k
  g_free(self->product_id);
742
1.01k
  G_OBJECT_CLASS(fu_synaptics_rmi_firmware_parent_class)->finalize(obj);
743
1.01k
}
744
745
static void
746
fu_synaptics_rmi_firmware_class_init(FuSynapticsRmiFirmwareClass *klass)
747
1
{
748
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
749
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
750
1
  fu_firmware_add_image_gtype(firmware_class, FU_TYPE_FIRMWARE);
751
1
  object_class->finalize = fu_synaptics_rmi_firmware_finalize;
752
1
  firmware_class->parse = fu_synaptics_rmi_firmware_parse;
753
1
  firmware_class->export = fu_synaptics_rmi_firmware_export;
754
1
  firmware_class->build = fu_synaptics_rmi_firmware_build;
755
1
  firmware_class->write = fu_synaptics_rmi_firmware_write;
756
1
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
757
1
  fu_firmware_set_images_max(firmware_class, RMI_IMG_MAX_CONTAINERS);
758
1
}
759
760
static gboolean
761
fu_synaptics_rmi_firmware_parse_sbl_container_v10(FuSynapticsRmiFirmware *self,
762
              FuInputStream *stream,
763
              const guint8 *buf,
764
              gsize bufsz,
765
              guint32 start_offset,
766
              FuFirmwareParseFlags flags,
767
              GError **error)
768
2.13k
{
769
2.13k
  guint32 cntr_addr;
770
2.13k
  guint32 offset;
771
2.13k
  guint32 cntrs_len;
772
2.13k
  g_autoptr(FuStructRmiContainerDescriptor) st_dsc = NULL;
773
774
2.13k
  if (!fu_memread_uint32_safe(buf, bufsz, start_offset, &cntr_addr, G_LITTLE_ENDIAN, error))
775
0
    return FALSE;
776
777
2.13k
  st_dsc = fu_struct_rmi_container_descriptor_parse_stream(stream, cntr_addr, error);
778
2.13k
  if (st_dsc == NULL) {
779
0
    g_prefix_error_literal(error, "RmiContainerDescriptor invalid: ");
780
0
    return FALSE;
781
0
  }
782
783
2.13k
  offset = fu_struct_rmi_container_descriptor_get_content_address(st_dsc);
784
2.13k
  cntrs_len = fu_struct_rmi_container_descriptor_get_content_length(st_dsc) / 4;
785
2.13k
  if (cntrs_len > RMI_IMG_MAX_CONTAINERS) {
786
9
    g_set_error(error,
787
9
          FWUPD_ERROR,
788
9
          FWUPD_ERROR_INVALID_FILE,
789
9
          "too many containers in file [%u], maximum is %u",
790
9
          cntrs_len,
791
9
          (guint)RMI_IMG_MAX_CONTAINERS);
792
9
    return FALSE;
793
9
  }
794
28.7k
  for (guint32 i = 0; i < cntrs_len; i++) {
795
26.7k
    guint32 addr_offset = offset + (i * 4);
796
26.7k
    guint16 container_id;
797
26.7k
    guint32 addr;
798
26.7k
    guint32 length;
799
26.7k
    guint32 content_addr;
800
26.7k
    guint32 signature_size;
801
26.7k
    g_autoptr(FuStructRmiContainerDescriptor) st_dsc2 = NULL;
802
803
26.7k
    if (!fu_memread_uint32_safe(buf, bufsz, addr_offset, &addr, G_LITTLE_ENDIAN, error))
804
0
      return FALSE;
805
806
26.7k
    st_dsc2 = fu_struct_rmi_container_descriptor_parse_stream(stream, addr, error);
807
26.7k
    if (st_dsc2 == NULL) {
808
45
      g_prefix_error_literal(error, "RmiContainerDescriptor invalid: ");
809
45
      return FALSE;
810
45
    }
811
812
26.6k
    container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc2);
813
26.6k
    content_addr = fu_struct_rmi_container_descriptor_get_content_address(st_dsc2);
814
26.6k
    length = fu_struct_rmi_container_descriptor_get_content_length(st_dsc2);
815
26.6k
    signature_size = fu_struct_rmi_container_descriptor_get_signature_size(st_dsc2);
816
817
26.6k
    g_debug("RmiContainerDescriptor 0x%02x @ 0x%x (len 0x%x) sig_size 0x%x",
818
26.6k
      container_id,
819
26.6k
      content_addr,
820
26.6k
      length,
821
26.6k
      signature_size);
822
26.6k
    if (length == 0) {
823
365
      g_debug("no SBL image found");
824
365
      continue;
825
365
    }
826
827
26.3k
    if (container_id == FU_RMI_CONTAINER_ID_BL_IMAGE) {
828
8.60k
      if (signature_size != 0)
829
8.28k
        g_debug("SBL signature size : 0x%x", signature_size);
830
831
8.60k
      if (!fu_synaptics_rmi_firmware_add_image_v10(self,
832
8.60k
                     "sbl",
833
8.60k
                     stream,
834
8.60k
                     content_addr,
835
8.60k
                     length,
836
8.60k
                     signature_size,
837
8.60k
                     flags,
838
8.60k
                     error)) {
839
84
        g_prefix_error_literal(error, "failed to add SBL image: ");
840
84
        return FALSE;
841
84
      }
842
8.60k
    }
843
26.3k
  }
844
845
  /* success */
846
1.99k
  return TRUE;
847
2.12k
}