Coverage Report

Created: 2026-06-10 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/synaptics-prometheus/fu-synaptics-prometheus-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2019 Richard Hughes <richard@hughsie.com>
3
 * Copyright 2019 Synaptics Inc
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 */
7
8
#include "config.h"
9
10
#include <string.h>
11
12
#include "fu-synaptics-prometheus-firmware.h"
13
#include "fu-synaptics-prometheus-struct.h"
14
15
struct _FuSynapticsPrometheusFirmware {
16
  FuFirmware parent_instance;
17
  guint32 product_id;
18
  guint32 signature_size;
19
};
20
21
838
G_DEFINE_TYPE(FuSynapticsPrometheusFirmware, fu_synaptics_prometheus_firmware, FU_TYPE_FIRMWARE)
22
838
23
838
/* use only first 12 bit of 16 bits as tag value */
24
4.42k
#define FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MAX 0xfff0
25
26
357
#define FU_SYNAPTICS_PROMETHEUS_FIRMWARE_COUNT_MAX 64
27
28
guint32
29
fu_synaptics_prometheus_firmware_get_product_id(FuSynapticsPrometheusFirmware *self)
30
0
{
31
0
  g_return_val_if_fail(FU_IS_SYNAPTICS_PROMETHEUS_FIRMWARE(self), 0x0);
32
0
  return self->product_id;
33
0
}
34
35
gboolean
36
fu_synaptics_prometheus_firmware_set_signature_size(FuSynapticsPrometheusFirmware *self,
37
                guint32 signature_size)
38
0
{
39
0
  g_return_val_if_fail(FU_IS_SYNAPTICS_PROMETHEUS_FIRMWARE(self), FALSE);
40
0
  self->signature_size = signature_size;
41
0
  return TRUE;
42
0
}
43
44
static void
45
fu_synaptics_prometheus_firmware_export(FuFirmware *firmware,
46
          FuFirmwareExportFlags flags,
47
          XbBuilderNode *bn)
48
0
{
49
0
  FuSynapticsPrometheusFirmware *self = FU_SYNAPTICS_PROMETHEUS_FIRMWARE(firmware);
50
0
  fu_xmlb_builder_insert_kx(bn, "product_id", self->product_id);
51
0
}
52
53
static gboolean
54
fu_synaptics_prometheus_firmware_parse(FuFirmware *firmware,
55
               GInputStream *stream,
56
               FuFirmwareParseFlags flags,
57
               GError **error)
58
357
{
59
357
  FuSynapticsPrometheusFirmware *self = FU_SYNAPTICS_PROMETHEUS_FIRMWARE(firmware);
60
357
  gsize offset = 0;
61
357
  gsize streamsz = 0;
62
63
357
  if (!fu_input_stream_size(stream, &streamsz, error))
64
0
    return FALSE;
65
357
  if (streamsz < self->signature_size + FU_STRUCT_SYNAPTICS_PROMETHEUS_HDR_SIZE) {
66
25
    g_set_error_literal(error,
67
25
            FWUPD_ERROR,
68
25
            FWUPD_ERROR_INVALID_DATA,
69
25
            "blob is too small to be firmware");
70
25
    return FALSE;
71
25
  }
72
332
  streamsz -= self->signature_size;
73
74
  /* parse each chunk */
75
4.54k
  while (offset < streamsz) {
76
4.42k
    guint32 hdrsz;
77
4.42k
    guint32 tag;
78
4.42k
    g_autoptr(FuFirmware) img = fu_firmware_new();
79
4.42k
    g_autoptr(FuFirmware) img_old = NULL;
80
4.42k
    g_autoptr(FuStructSynapticsPrometheusHdr) st_hdr = NULL;
81
4.42k
    g_autoptr(GInputStream) partial_stream = NULL;
82
83
    /* verify item header */
84
4.42k
    st_hdr = fu_struct_synaptics_prometheus_hdr_parse_stream(stream, offset, error);
85
4.42k
    if (st_hdr == NULL)
86
0
      return FALSE;
87
4.42k
    tag = fu_struct_synaptics_prometheus_hdr_get_tag(st_hdr);
88
4.42k
    if (tag >= FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MAX) {
89
10
      g_set_error(error,
90
10
            FWUPD_ERROR,
91
10
            FWUPD_ERROR_INVALID_DATA,
92
10
            "tag 0x%04x is too large",
93
10
            tag);
94
10
      return FALSE;
95
10
    }
96
97
    /* sanity check */
98
4.41k
    img_old = fu_firmware_get_image_by_idx(firmware, tag, NULL);
99
4.41k
    if (img_old != NULL) {
100
17
      g_set_error(error,
101
17
            FWUPD_ERROR,
102
17
            FWUPD_ERROR_INVALID_DATA,
103
17
            "tag 0x%04x already present in image",
104
17
            tag);
105
17
      return FALSE;
106
17
    }
107
4.39k
    hdrsz = fu_struct_synaptics_prometheus_hdr_get_bufsz(st_hdr);
108
4.39k
    if (hdrsz == 0) {
109
22
      g_set_error(error,
110
22
            FWUPD_ERROR,
111
22
            FWUPD_ERROR_INVALID_DATA,
112
22
            "empty header for tag 0x%04x",
113
22
            tag);
114
22
      return FALSE;
115
22
    }
116
4.37k
    if (!fu_size_checked_inc(&offset, st_hdr->buf->len, error)) {
117
0
      g_prefix_error(error, "overflow at tag 0x%04x: ", tag);
118
0
      return FALSE;
119
0
    }
120
4.37k
    partial_stream = fu_partial_input_stream_new(stream, offset, hdrsz, error);
121
4.37k
    if (partial_stream == NULL)
122
157
      return FALSE;
123
4.21k
    if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
124
0
      return FALSE;
125
4.21k
    g_debug("adding 0x%04x (%s) with size 0x%04x",
126
4.21k
      tag,
127
4.21k
      fu_synaptics_prometheus_firmware_tag_to_string(tag),
128
4.21k
      hdrsz);
129
4.21k
    fu_firmware_set_idx(img, tag);
130
4.21k
    fu_firmware_set_id(img, fu_synaptics_prometheus_firmware_tag_to_string(tag));
131
4.21k
    if (!fu_firmware_add_image(firmware, img, error))
132
2
      return FALSE;
133
134
    /* metadata */
135
4.21k
    if (tag == FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MFW_UPDATE_HEADER) {
136
148
      g_autofree gchar *version = NULL;
137
148
      g_autoptr(FuStructSynapticsPrometheusMfwHdr) st_mfw = NULL;
138
148
      st_mfw = fu_struct_synaptics_prometheus_mfw_hdr_parse_stream(stream,
139
148
                         offset,
140
148
                         error);
141
148
      if (st_mfw == NULL)
142
0
        return FALSE;
143
148
      self->product_id =
144
148
          fu_struct_synaptics_prometheus_mfw_hdr_get_product(st_mfw);
145
148
      version = g_strdup_printf(
146
148
          "%u.%u",
147
148
          fu_struct_synaptics_prometheus_mfw_hdr_get_vmajor(st_mfw),
148
148
          fu_struct_synaptics_prometheus_mfw_hdr_get_vminor(st_mfw));
149
148
      fu_firmware_set_version(firmware, version);
150
148
    }
151
152
    /* next item */
153
4.21k
    if (!fu_size_checked_inc(&offset, hdrsz, error))
154
0
      return FALSE;
155
4.21k
  }
156
124
  return TRUE;
157
332
}
158
159
static GByteArray *
160
fu_synaptics_prometheus_firmware_write(FuFirmware *firmware, GError **error)
161
124
{
162
124
  FuSynapticsPrometheusFirmware *self = FU_SYNAPTICS_PROMETHEUS_FIRMWARE(firmware);
163
124
  g_autoptr(GByteArray) buf = g_byte_array_new();
164
124
  g_autoptr(FuStructSynapticsPrometheusHdr) st_hdr = fu_struct_synaptics_prometheus_hdr_new();
165
124
  g_autoptr(FuStructSynapticsPrometheusMfwHdr) st_mfw =
166
124
      fu_struct_synaptics_prometheus_mfw_hdr_new();
167
124
  g_autoptr(GBytes) payload = NULL;
168
169
  /* add header */
170
124
  fu_struct_synaptics_prometheus_hdr_set_tag(
171
124
      st_hdr,
172
124
      FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MFW_UPDATE_HEADER);
173
124
  fu_struct_synaptics_prometheus_hdr_set_bufsz(st_hdr, st_mfw->buf->len);
174
124
  g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len);
175
124
  fu_struct_synaptics_prometheus_mfw_hdr_set_product(st_mfw, self->product_id);
176
124
  fu_byte_array_append_array(buf, st_mfw->buf);
177
178
  /* add payload */
179
124
  payload = fu_firmware_get_bytes_with_patches(firmware, error);
180
124
  if (payload == NULL)
181
124
    return NULL;
182
0
  fu_struct_synaptics_prometheus_hdr_set_tag(
183
0
      st_hdr,
184
0
      FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MFW_UPDATE_PAYLOAD);
185
0
  fu_struct_synaptics_prometheus_hdr_set_bufsz(st_hdr, g_bytes_get_size(payload));
186
0
  g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len);
187
0
  fu_byte_array_append_bytes(buf, payload);
188
189
  /* add signature */
190
0
  for (guint i = 0; i < self->signature_size; i++)
191
0
    fu_byte_array_append_uint8(buf, 0xff);
192
0
  return g_steal_pointer(&buf);
193
124
}
194
195
static gboolean
196
fu_synaptics_prometheus_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
197
0
{
198
0
  FuSynapticsPrometheusFirmware *self = FU_SYNAPTICS_PROMETHEUS_FIRMWARE(firmware);
199
0
  guint64 tmp;
200
201
  /* simple properties */
202
0
  tmp = xb_node_query_text_as_uint(n, "product_id", NULL);
203
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
204
0
    self->product_id = tmp;
205
206
  /* success */
207
0
  return TRUE;
208
0
}
209
210
static void
211
fu_synaptics_prometheus_firmware_init(FuSynapticsPrometheusFirmware *self)
212
357
{
213
357
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
214
357
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
215
357
  fu_firmware_set_images_max(FU_FIRMWARE(self), FU_SYNAPTICS_PROMETHEUS_FIRMWARE_COUNT_MAX);
216
357
  fu_firmware_set_size_max(FU_FIRMWARE(self), 16 * FU_MB);
217
357
  self->signature_size = FU_SYNAPTICS_PROMETHEUS_FIRMWARE_PROMETHEUS_SIGSIZE;
218
357
}
219
220
static void
221
fu_synaptics_prometheus_firmware_class_init(FuSynapticsPrometheusFirmwareClass *klass)
222
1
{
223
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
224
1
  firmware_class->parse = fu_synaptics_prometheus_firmware_parse;
225
1
  firmware_class->write = fu_synaptics_prometheus_firmware_write;
226
1
  firmware_class->export = fu_synaptics_prometheus_firmware_export;
227
1
  firmware_class->build = fu_synaptics_prometheus_firmware_build;
228
1
}
229
230
FuFirmware *
231
fu_synaptics_prometheus_firmware_new(void)
232
0
{
233
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_PROMETHEUS_FIRMWARE, NULL));
234
0
}