Coverage Report

Created: 2025-07-18 06:26

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