Coverage Report

Created: 2026-04-28 06:49

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