Coverage Report

Created: 2026-09-03 07:14

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
736
G_DEFINE_TYPE(FuSynapticsPrometheusFirmware, fu_synaptics_prometheus_firmware, FU_TYPE_FIRMWARE)
22
736
23
736
/* use only first 12 bit of 16 bits as tag value */
24
3.46k
#define FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MAX 0xfff0
25
26
1
#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
               FuInputStream *stream,
56
               FuFirmwareParseFlags flags,
57
               GError **error)
58
311
{
59
311
  FuSynapticsPrometheusFirmware *self = FU_SYNAPTICS_PROMETHEUS_FIRMWARE(firmware);
60
311
  gsize offset = 0;
61
311
  gsize streamsz = 0;
62
63
311
  if (!fu_input_stream_size(stream, &streamsz, error))
64
0
    return FALSE;
65
311
  if (streamsz < self->signature_size + FU_STRUCT_SYNAPTICS_PROMETHEUS_HDR_SIZE) {
66
24
    g_set_error_literal(error,
67
24
            FWUPD_ERROR,
68
24
            FWUPD_ERROR_INVALID_DATA,
69
24
            "blob is too small to be firmware");
70
24
    return FALSE;
71
24
  }
72
287
  streamsz -= self->signature_size;
73
74
  /* parse each chunk */
75
3.57k
  while (offset < streamsz) {
76
3.46k
    guint32 hdrsz;
77
3.46k
    guint32 tag;
78
3.46k
    g_autoptr(FuFirmware) img = fu_firmware_new();
79
3.46k
    g_autoptr(FuFirmware) img_old = NULL;
80
3.46k
    g_autoptr(FuStructSynapticsPrometheusHdr) st_hdr = NULL;
81
3.46k
    g_autoptr(FuInputStream) partial_stream = NULL;
82
83
    /* verify item header */
84
3.46k
    st_hdr = fu_struct_synaptics_prometheus_hdr_parse_stream(stream, offset, error);
85
3.46k
    if (st_hdr == NULL)
86
0
      return FALSE;
87
3.46k
    tag = fu_struct_synaptics_prometheus_hdr_get_tag(st_hdr);
88
3.46k
    if (tag >= FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MAX) {
89
3
      g_set_error(error,
90
3
            FWUPD_ERROR,
91
3
            FWUPD_ERROR_INVALID_DATA,
92
3
            "tag 0x%04x is too large",
93
3
            tag);
94
3
      return FALSE;
95
3
    }
96
97
    /* sanity check */
98
3.45k
    img_old = fu_firmware_get_image_by_idx(firmware, tag, NULL);
99
3.45k
    if (img_old != NULL) {
100
12
      g_set_error(error,
101
12
            FWUPD_ERROR,
102
12
            FWUPD_ERROR_INVALID_DATA,
103
12
            "tag 0x%04x already present in image",
104
12
            tag);
105
12
      return FALSE;
106
12
    }
107
3.44k
    hdrsz = fu_struct_synaptics_prometheus_hdr_get_bufsz(st_hdr);
108
3.44k
    if (hdrsz == 0) {
109
14
      g_set_error(error,
110
14
            FWUPD_ERROR,
111
14
            FWUPD_ERROR_INVALID_DATA,
112
14
            "empty header for tag 0x%04x",
113
14
            tag);
114
14
      return FALSE;
115
14
    }
116
3.43k
    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
3.43k
    partial_stream = fu_partial_input_stream_new(stream, offset, hdrsz, error);
121
3.43k
    if (partial_stream == NULL)
122
145
      return FALSE;
123
3.28k
    if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
124
0
      return FALSE;
125
3.28k
    g_debug("adding 0x%04x (%s) with size 0x%04x",
126
3.28k
      tag,
127
3.28k
      fu_synaptics_prometheus_firmware_tag_to_string(tag),
128
3.28k
      hdrsz);
129
3.28k
    fu_firmware_set_idx(img, tag);
130
3.28k
    fu_firmware_set_id(img, fu_synaptics_prometheus_firmware_tag_to_string(tag));
131
3.28k
    if (!fu_firmware_add_image(firmware, img, error))
132
1
      return FALSE;
133
134
    /* metadata */
135
3.28k
    if (tag == FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MFW_UPDATE_HEADER) {
136
113
      g_autofree gchar *version = NULL;
137
113
      g_autoptr(FuStructSynapticsPrometheusMfwHdr) st_mfw = NULL;
138
113
      st_mfw = fu_struct_synaptics_prometheus_mfw_hdr_parse_stream(stream,
139
113
                         offset,
140
113
                         error);
141
113
      if (st_mfw == NULL)
142
0
        return FALSE;
143
113
      self->product_id =
144
113
          fu_struct_synaptics_prometheus_mfw_hdr_get_product(st_mfw);
145
113
      version = g_strdup_printf(
146
113
          "%u.%u",
147
113
          fu_struct_synaptics_prometheus_mfw_hdr_get_vmajor(st_mfw),
148
113
          fu_struct_synaptics_prometheus_mfw_hdr_get_vminor(st_mfw));
149
113
      fu_firmware_set_version(firmware, version);
150
113
    }
151
152
    /* next item */
153
3.28k
    if (!fu_size_checked_inc(&offset, hdrsz, error))
154
0
      return FALSE;
155
3.28k
  }
156
112
  return TRUE;
157
287
}
158
159
static GByteArray *
160
fu_synaptics_prometheus_firmware_write(FuFirmware *firmware, GError **error)
161
112
{
162
112
  FuSynapticsPrometheusFirmware *self = FU_SYNAPTICS_PROMETHEUS_FIRMWARE(firmware);
163
112
  g_autoptr(GByteArray) buf = g_byte_array_new();
164
112
  g_autoptr(FuStructSynapticsPrometheusHdr) st_hdr = fu_struct_synaptics_prometheus_hdr_new();
165
112
  g_autoptr(FuStructSynapticsPrometheusMfwHdr) st_mfw =
166
112
      fu_struct_synaptics_prometheus_mfw_hdr_new();
167
112
  g_autoptr(GBytes) payload = NULL;
168
169
  /* add header */
170
112
  fu_struct_synaptics_prometheus_hdr_set_tag(
171
112
      st_hdr,
172
112
      FU_SYNAPTICS_PROMETHEUS_FIRMWARE_TAG_MFW_UPDATE_HEADER);
173
112
  fu_struct_synaptics_prometheus_hdr_set_bufsz(st_hdr, st_mfw->buf->len);
174
112
  g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len);
175
112
  fu_struct_synaptics_prometheus_mfw_hdr_set_product(st_mfw, self->product_id);
176
112
  fu_byte_array_append_array(buf, st_mfw->buf);
177
178
  /* add payload */
179
112
  payload = fu_firmware_get_bytes_with_patches(firmware, error);
180
112
  if (payload == NULL)
181
112
    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
112
}
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
311
{
213
311
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
214
311
  self->signature_size = FU_SYNAPTICS_PROMETHEUS_FIRMWARE_PROMETHEUS_SIGSIZE;
215
311
}
216
217
static void
218
fu_synaptics_prometheus_firmware_class_init(FuSynapticsPrometheusFirmwareClass *klass)
219
1
{
220
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
221
1
  fu_firmware_add_image_gtype(firmware_class, FU_TYPE_FIRMWARE);
222
1
  firmware_class->parse = fu_synaptics_prometheus_firmware_parse;
223
1
  firmware_class->write = fu_synaptics_prometheus_firmware_write;
224
1
  firmware_class->export = fu_synaptics_prometheus_firmware_export;
225
1
  firmware_class->build = fu_synaptics_prometheus_firmware_build;
226
1
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
227
1
  fu_firmware_set_images_max(firmware_class, FU_SYNAPTICS_PROMETHEUS_FIRMWARE_COUNT_MAX);
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
}