Coverage Report

Created: 2025-08-24 07:10

/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
233
#define RMI_IMG_FW_OFFSET 0x100
40
41
835
#define RMI_IMG_V10_CNTR_ADDR_OFFSET 0x0c
42
2.78k
#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
17.3k
{
53
17.3k
  g_autoptr(FuFirmware) img = fu_firmware_new();
54
17.3k
  g_autoptr(GInputStream) partial_stream = NULL;
55
17.3k
  partial_stream = fu_partial_input_stream_new(stream, offset, bufsz, error);
56
17.3k
  if (partial_stream == NULL)
57
132
    return FALSE;
58
17.2k
  if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
59
0
    return FALSE;
60
17.2k
  fu_firmware_set_id(img, id);
61
17.2k
  return fu_firmware_add_image_full(firmware, img, error);
62
17.2k
}
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
17.2k
{
74
17.2k
  if (!fu_synaptics_rmi_firmware_add_image(firmware, id, stream, offset, bufsz, flags, error))
75
3
    return FALSE;
76
17.2k
  if (sig_sz != 0) {
77
15.6k
    g_autoptr(GInputStream) partial_stream = NULL;
78
15.6k
    g_autoptr(FuFirmware) img = fu_firmware_new();
79
15.6k
    g_autofree gchar *sig_id = NULL;
80
81
15.6k
    partial_stream = fu_partial_input_stream_new(stream, offset + bufsz, sig_sz, error);
82
15.6k
    if (partial_stream == NULL)
83
65
      return FALSE;
84
15.6k
    if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
85
0
      return FALSE;
86
15.6k
    sig_id = g_strdup_printf("%s-signature", id);
87
15.6k
    fu_firmware_set_id(img, sig_id);
88
15.6k
    fu_firmware_add_image(firmware, img);
89
15.6k
  }
90
17.1k
  return TRUE;
91
17.2k
}
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
835
{
118
835
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
119
835
  guint16 container_id;
120
835
  guint32 cntrs_len;
121
835
  guint32 offset;
122
835
  guint32 cntr_addr;
123
835
  guint8 product_id[RMI_PRODUCT_ID_LENGTH] = {0x0};
124
835
  gsize bufsz = 0;
125
835
  const guint8 *buf;
126
835
  guint32 signature_size;
127
835
  g_autoptr(GByteArray) st_dsc = NULL;
128
835
  g_autoptr(GBytes) fw = NULL;
129
130
  /* maybe stream later */
131
835
  fw = fu_input_stream_read_bytes(stream, 0, G_MAXSIZE, NULL, error);
132
835
  if (fw == NULL)
133
0
    return FALSE;
134
835
  buf = g_bytes_get_data(fw, &bufsz);
135
136
835
  if (!fu_memread_uint32_safe(buf,
137
835
            bufsz,
138
835
            RMI_IMG_V10_CNTR_ADDR_OFFSET,
139
835
            &cntr_addr,
140
835
            G_LITTLE_ENDIAN,
141
835
            error))
142
0
    return FALSE;
143
835
  g_debug("v10 RmiContainerDescriptor at 0x%x", cntr_addr);
144
835
  st_dsc = fu_struct_rmi_container_descriptor_parse_stream(stream, cntr_addr, error);
145
835
  if (st_dsc == NULL) {
146
24
    g_prefix_error_literal(error, "RmiContainerDescriptor invalid: ");
147
24
    return FALSE;
148
24
  }
149
811
  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
811
  container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc);
157
811
  if (container_id != FU_RMI_CONTAINER_ID_TOP_LEVEL) {
158
17
    g_set_error(error,
159
17
          FWUPD_ERROR,
160
17
          FWUPD_ERROR_INVALID_FILE,
161
17
          "toplevel container_id invalid, got 0x%x expected 0x%x",
162
17
          (guint)container_id,
163
17
          (guint)FU_RMI_CONTAINER_ID_TOP_LEVEL);
164
17
    return FALSE;
165
17
  }
166
794
  offset = fu_struct_rmi_container_descriptor_get_content_address(st_dsc);
167
794
  if (offset > bufsz - sizeof(guint32) - st_dsc->len) {
168
34
    g_set_error(error,
169
34
          FWUPD_ERROR,
170
34
          FWUPD_ERROR_INVALID_FILE,
171
34
          "image offset invalid, got 0x%x, size 0x%x",
172
34
          (guint)offset,
173
34
          (guint)bufsz);
174
34
    return FALSE;
175
34
  }
176
760
  cntrs_len = fu_struct_rmi_container_descriptor_get_content_length(st_dsc) / 4;
177
760
  if (cntrs_len > RMI_IMG_MAX_CONTAINERS) {
178
42
    g_set_error(error,
179
42
          FWUPD_ERROR,
180
42
          FWUPD_ERROR_INVALID_FILE,
181
42
          "too many containers in file [%u], maximum is %u",
182
42
          cntrs_len,
183
42
          (guint)RMI_IMG_MAX_CONTAINERS);
184
42
    return FALSE;
185
42
  }
186
718
  g_debug("offset=0x%x (cntrs_len=%u)", offset, cntrs_len);
187
188
32.5k
  for (guint32 i = 0; i < cntrs_len; i++) {
189
32.4k
    guint32 content_addr;
190
32.4k
    guint32 addr;
191
32.4k
    guint32 length;
192
32.4k
    g_autoptr(GByteArray) st_dsc2 = NULL;
193
194
32.4k
    if (!fu_memread_uint32_safe(buf, bufsz, offset, &addr, G_LITTLE_ENDIAN, error))
195
183
      return FALSE;
196
32.3k
    g_debug("parsing RmiContainerDescriptor at 0x%x", addr);
197
198
32.3k
    st_dsc2 = fu_struct_rmi_container_descriptor_parse_stream(stream, addr, error);
199
32.3k
    if (st_dsc2 == NULL)
200
222
      return FALSE;
201
32.0k
    container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc2);
202
32.0k
    content_addr = fu_struct_rmi_container_descriptor_get_content_address(st_dsc2);
203
32.0k
    length = fu_struct_rmi_container_descriptor_get_content_length(st_dsc2);
204
32.0k
    signature_size = fu_struct_rmi_container_descriptor_get_signature_size(st_dsc2);
205
32.0k
    g_debug("RmiContainerDescriptor 0x%02x @ 0x%x (len 0x%x) sig_size 0x%x",
206
32.0k
      container_id,
207
32.0k
      content_addr,
208
32.0k
      length,
209
32.0k
      signature_size);
210
32.0k
    if (length == 0 || length > bufsz) {
211
117
      g_set_error(error,
212
117
            FWUPD_ERROR,
213
117
            FWUPD_ERROR_INVALID_FILE,
214
117
            "length invalid, length 0x%x, size 0x%x",
215
117
            (guint)length,
216
117
            (guint)bufsz);
217
117
      return FALSE;
218
117
    }
219
31.9k
    if (content_addr > bufsz - length) {
220
29
      g_set_error(error,
221
29
            FWUPD_ERROR,
222
29
            FWUPD_ERROR_INVALID_FILE,
223
29
            "address invalid, got 0x%x (length 0x%x), size 0x%x",
224
29
            (guint)content_addr,
225
29
            (guint)length,
226
29
            (guint)bufsz);
227
29
      return FALSE;
228
29
    }
229
31.9k
    switch (container_id) {
230
561
    case FU_RMI_CONTAINER_ID_BL:
231
561
      if (!fu_memread_uint8_safe(buf,
232
561
               bufsz,
233
561
               content_addr,
234
561
               &self->bootloader_version,
235
561
               error))
236
0
        return FALSE;
237
561
      break;
238
2.27k
    case FU_RMI_CONTAINER_ID_UI:
239
5.17k
    case FU_RMI_CONTAINER_ID_CORE_CODE:
240
5.17k
      if (!fu_synaptics_rmi_firmware_add_image_v10(firmware,
241
5.17k
                     "ui",
242
5.17k
                     stream,
243
5.17k
                     content_addr,
244
5.17k
                     length,
245
5.17k
                     signature_size,
246
5.17k
                     flags,
247
5.17k
                     error))
248
23
        return FALSE;
249
5.15k
      break;
250
5.15k
    case FU_RMI_CONTAINER_ID_FLASH_CONFIG:
251
3.07k
      if (!fu_synaptics_rmi_firmware_add_image_v10(firmware,
252
3.07k
                     "flash-config",
253
3.07k
                     stream,
254
3.07k
                     content_addr,
255
3.07k
                     length,
256
3.07k
                     signature_size,
257
3.07k
                     flags,
258
3.07k
                     error))
259
7
        return FALSE;
260
3.06k
      break;
261
3.06k
    case FU_RMI_CONTAINER_ID_UI_CONFIG:
262
3.98k
    case FU_RMI_CONTAINER_ID_CORE_CONFIG:
263
3.98k
      if (!fu_synaptics_rmi_firmware_add_image_v10(firmware,
264
3.98k
                     "config",
265
3.98k
                     stream,
266
3.98k
                     content_addr,
267
3.98k
                     length,
268
3.98k
                     signature_size,
269
3.98k
                     flags,
270
3.98k
                     error))
271
6
        return FALSE;
272
3.98k
      break;
273
3.98k
    case FU_RMI_CONTAINER_ID_FIXED_LOCATION_DATA:
274
1.94k
      if (!fu_synaptics_rmi_firmware_add_image_v10(firmware,
275
1.94k
                     "fixed-location-data",
276
1.94k
                     stream,
277
1.94k
                     content_addr,
278
1.94k
                     length,
279
1.94k
                     signature_size,
280
1.94k
                     flags,
281
1.94k
                     error))
282
9
        return FALSE;
283
1.93k
      break;
284
1.93k
    case FU_RMI_CONTAINER_ID_EXTERNAL_TOUCH_AFE_CONFIG:
285
573
      if (!fu_synaptics_rmi_firmware_add_image_v10(firmware,
286
573
                     "afe-config",
287
573
                     stream,
288
573
                     content_addr,
289
573
                     length,
290
573
                     signature_size,
291
573
                     flags,
292
573
                     error))
293
13
        return FALSE;
294
560
      break;
295
2.48k
    case FU_RMI_CONTAINER_ID_DISPLAY_CONFIG:
296
2.48k
      if (!fu_synaptics_rmi_firmware_add_image_v10(firmware,
297
2.48k
                     "display-config",
298
2.48k
                     stream,
299
2.48k
                     content_addr,
300
2.48k
                     length,
301
2.48k
                     signature_size,
302
2.48k
                     flags,
303
2.48k
                     error))
304
10
        return FALSE;
305
2.47k
      break;
306
2.47k
    case FU_RMI_CONTAINER_ID_GENERAL_INFORMATION:
307
711
      if (length < 0x18 + RMI_PRODUCT_ID_LENGTH) {
308
8
        g_set_error(error,
309
8
              FWUPD_ERROR,
310
8
              FWUPD_ERROR_INVALID_FILE,
311
8
              "content_addr invalid, got 0x%x (length 0x%x)",
312
8
              content_addr,
313
8
              (guint)length);
314
8
        return FALSE;
315
8
      }
316
711
      g_clear_pointer(&self->product_id, g_free);
317
703
      self->io = 1;
318
703
      if (!fu_memread_uint32_safe(buf,
319
703
                bufsz,
320
703
                content_addr,
321
703
                &self->package_id,
322
703
                G_LITTLE_ENDIAN,
323
703
                error))
324
0
        return FALSE;
325
703
      if (!fu_memread_uint32_safe(buf,
326
703
                bufsz,
327
703
                content_addr + 0x04,
328
703
                &self->build_id,
329
703
                G_LITTLE_ENDIAN,
330
703
                error))
331
0
        return FALSE;
332
703
      if (!fu_memcpy_safe(product_id,
333
703
              sizeof(product_id),
334
703
              0x0, /* dst */
335
703
              buf,
336
703
              bufsz,
337
703
              content_addr + 0x18, /* src */
338
703
              sizeof(product_id),
339
703
              error))
340
0
        return FALSE;
341
703
      break;
342
13.4k
    default:
343
13.4k
      g_debug("unsupported container %s [0x%02x]",
344
13.4k
        fu_rmi_container_id_to_string(container_id),
345
13.4k
        container_id);
346
13.4k
      break;
347
31.9k
    }
348
31.8k
    offset += 4;
349
31.8k
  }
350
91
  if (product_id[0] != '\0') {
351
4
    g_free(self->product_id);
352
4
    self->product_id = g_strndup((const gchar *)product_id, sizeof(product_id));
353
4
  }
354
91
  return TRUE;
355
718
}
356
357
static gboolean
358
fu_synaptics_rmi_firmware_parse_v0x(FuFirmware *firmware,
359
            GInputStream *stream,
360
            FuFirmwareParseFlags flags,
361
            GError **error)
362
137
{
363
137
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
364
137
  guint32 cfg_sz;
365
137
  guint32 img_sz;
366
137
  g_autoptr(GByteArray) st_img = NULL;
367
368
  /* main firmware */
369
137
  st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error);
370
137
  if (st_img == NULL)
371
0
    return FALSE;
372
137
  img_sz = fu_struct_rmi_img_get_image_size(st_img);
373
137
  if (img_sz > 0) {
374
    /* payload, then signature appended */
375
76
    if (self->sig_size > 0) {
376
60
      guint32 sig_offset = img_sz - self->sig_size;
377
60
      if (!fu_synaptics_rmi_firmware_add_image(firmware,
378
60
                 "sig",
379
60
                 stream,
380
60
                 RMI_IMG_FW_OFFSET + sig_offset,
381
60
                 self->sig_size,
382
60
                 flags,
383
60
                 error))
384
55
        return FALSE;
385
60
    }
386
21
    if (!fu_synaptics_rmi_firmware_add_image(firmware,
387
21
               "ui",
388
21
               stream,
389
21
               RMI_IMG_FW_OFFSET,
390
21
               img_sz,
391
21
               flags,
392
21
               error))
393
17
      return FALSE;
394
21
  }
395
396
  /* config */
397
65
  cfg_sz = fu_struct_rmi_img_get_config_size(st_img);
398
65
  if (cfg_sz > 0) {
399
61
    if (!fu_synaptics_rmi_firmware_add_image(firmware,
400
61
               "config",
401
61
               stream,
402
61
               RMI_IMG_FW_OFFSET + img_sz,
403
61
               cfg_sz,
404
61
               flags,
405
61
               error))
406
60
      return FALSE;
407
61
  }
408
5
  return TRUE;
409
65
}
410
411
static gboolean
412
fu_synaptics_rmi_firmware_parse(FuFirmware *firmware,
413
        GInputStream *stream,
414
        FuFirmwareParseFlags flags,
415
        GError **error)
416
1.97k
{
417
1.97k
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
418
1.97k
  gsize bufsz = 0;
419
1.97k
  const guint8 *buf;
420
1.97k
  g_autoptr(GByteArray) st_img = NULL;
421
1.97k
  g_autoptr(GBytes) fw = NULL;
422
423
  /* maybe stream later */
424
1.97k
  fw = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error);
425
1.97k
  if (fw == NULL)
426
0
    return FALSE;
427
1.97k
  buf = g_bytes_get_data(fw, &bufsz);
428
429
  /* sanity check */
430
1.97k
  st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error);
431
1.97k
  if (st_img == NULL)
432
56
    return FALSE;
433
1.92k
  if (bufsz % 2 != 0) {
434
60
    g_set_error_literal(error,
435
60
            FWUPD_ERROR,
436
60
            FWUPD_ERROR_INVALID_FILE,
437
60
            "data not aligned to 16 bits");
438
60
    return FALSE;
439
60
  }
440
1.86k
  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.86k
  self->checksum = fu_struct_rmi_img_get_checksum(st_img);
450
1.86k
  if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) {
451
934
    guint32 checksum_calculated =
452
934
        fu_synaptics_rmi_generate_checksum(buf + 4, bufsz - 4);
453
934
    if (self->checksum != checksum_calculated) {
454
856
      g_set_error(error,
455
856
            FWUPD_ERROR,
456
856
            FWUPD_ERROR_INVALID_FILE,
457
856
            "checksum verification failed, got 0x%08x, actual 0x%08x",
458
856
            (guint)self->checksum,
459
856
            (guint)checksum_calculated);
460
856
      return FALSE;
461
856
    }
462
934
  }
463
464
  /* parse legacy image */
465
1.86k
  g_clear_pointer(&self->product_id, g_free);
466
1.00k
  self->io = fu_struct_rmi_img_get_io_offset(st_img);
467
1.00k
  self->bootloader_version = fu_struct_rmi_img_get_bootloader_version(st_img);
468
1.00k
  if (self->io == 1) {
469
129
    self->build_id = fu_struct_rmi_img_get_fw_build_id(st_img);
470
129
    self->package_id = fu_struct_rmi_img_get_package_id(st_img);
471
129
  }
472
1.00k
  self->product_id = fu_struct_rmi_img_get_product_id(st_img);
473
1.00k
  self->product_info = fu_struct_rmi_img_get_product_info(st_img);
474
1.00k
  fu_firmware_set_size(firmware, fu_struct_rmi_img_get_image_size(st_img));
475
476
  /* parse partitions, but ignore lockdown */
477
1.00k
  switch (self->bootloader_version) {
478
28
  case 2:
479
96
  case 3:
480
106
  case 4:
481
134
  case 5:
482
137
  case 6:
483
137
    if ((self->io & 0x10) >> 1)
484
110
      self->sig_size = fu_struct_rmi_img_get_signature_size(st_img);
485
137
    if (!fu_synaptics_rmi_firmware_parse_v0x(firmware, stream, flags, error))
486
132
      return FALSE;
487
5
    self->kind = RMI_FIRMWARE_KIND_0X;
488
5
    break;
489
682
  case 16:
490
835
  case 17:
491
835
    if (!fu_synaptics_rmi_firmware_parse_v10(firmware, stream, flags, error))
492
744
      return FALSE;
493
91
    self->kind = RMI_FIRMWARE_KIND_10;
494
91
    break;
495
35
  default:
496
35
    g_set_error(error,
497
35
          FWUPD_ERROR,
498
35
          FWUPD_ERROR_INVALID_FILE,
499
35
          "unsupported image version 0x%02x",
500
35
          self->bootloader_version);
501
35
    return FALSE;
502
1.00k
  }
503
504
  /* success */
505
96
  return TRUE;
506
1.00k
}
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
5
{
517
5
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
518
5
  gsize bufsz = 0;
519
5
  guint32 csum;
520
5
  g_autoptr(FuFirmware) img = NULL;
521
5
  g_autoptr(GByteArray) buf = g_byte_array_new();
522
5
  g_autoptr(GByteArray) st_img = fu_struct_rmi_img_new();
523
5
  g_autoptr(GBytes) buf_blob = NULL;
524
525
  /* default image */
526
5
  img = fu_firmware_get_image_by_id(firmware, "ui", error);
527
5
  if (img == NULL)
528
3
    return NULL;
529
2
  buf_blob = fu_firmware_write(img, error);
530
2
  if (buf_blob == NULL)
531
2
    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
91
{
561
91
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
562
91
  gsize bufsz;
563
91
  guint32 csum;
564
91
  g_autoptr(FuFirmware) img = NULL;
565
91
  g_autoptr(GByteArray) buf = g_byte_array_new();
566
91
  g_autoptr(GByteArray) desc_hdr = fu_struct_rmi_container_descriptor_new();
567
91
  g_autoptr(GByteArray) desc = fu_struct_rmi_container_descriptor_new();
568
91
  g_autoptr(GBytes) buf_blob = NULL;
569
570
  /* header | desc_hdr | offset_table | desc | flash_config |
571
   *        \0x0       \0x20          \0x24  \0x44          |0x48 */
572
91
  guint32 offset_table[] = {/* offset to first descriptor */
573
91
          GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x24)}; /* nocheck:blocked */
574
91
  fu_struct_rmi_container_descriptor_set_container_id(desc, FU_RMI_CONTAINER_ID_FLASH_CONFIG);
575
91
  fu_struct_rmi_container_descriptor_set_content_address(desc, RMI_IMG_FW_OFFSET + 0x44);
576
577
  /* default image */
578
91
  img = fu_firmware_get_image_by_id(firmware, "ui", error);
579
91
  if (img == NULL)
580
62
    return NULL;
581
29
  buf_blob = fu_firmware_write(img, error);
582
29
  if (buf_blob == NULL)
583
29
    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
96
{
684
96
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware);
685
686
  /* two supported container formats */
687
96
  if (self->kind == RMI_FIRMWARE_KIND_0X)
688
5
    return fu_synaptics_rmi_firmware_write_v0x(firmware, error);
689
91
  if (self->kind == RMI_FIRMWARE_KIND_10)
690
91
    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
91
}
696
697
static void
698
fu_synaptics_rmi_firmware_init(FuSynapticsRmiFirmware *self)
699
1.97k
{
700
1.97k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
701
1.97k
  fu_firmware_set_images_max(FU_FIRMWARE(self), RMI_IMG_MAX_CONTAINERS);
702
1.97k
}
703
704
static void
705
fu_synaptics_rmi_firmware_finalize(GObject *obj)
706
1.97k
{
707
1.97k
  FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(obj);
708
1.97k
  g_free(self->product_id);
709
1.97k
  G_OBJECT_CLASS(fu_synaptics_rmi_firmware_parent_class)->finalize(obj);
710
1.97k
}
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
}