Coverage Report

Created: 2025-06-22 06:29

/src/fwupd/plugins/synaptics-prometheus/fu-synaprom-firmware.c
Line
Count
Source (jump to first uncovered line)
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-synaprom-firmware.h"
13
#include "fu-synaprom-struct.h"
14
15
struct _FuSynapromFirmware {
16
  FuFirmware parent_instance;
17
  guint32 product_id;
18
  guint32 signature_size;
19
};
20
21
G_DEFINE_TYPE(FuSynapromFirmware, fu_synaprom_firmware, FU_TYPE_FIRMWARE)
22
23
/* use only first 12 bit of 16 bits as tag value */
24
4.80k
#define FU_SYNAPROM_FIRMWARE_TAG_MAX 0xfff0
25
26
394
#define FU_SYNAPROM_FIRMWARE_COUNT_MAX 64
27
28
guint32
29
fu_synaprom_firmware_get_product_id(FuSynapromFirmware *self)
30
0
{
31
0
  g_return_val_if_fail(FU_IS_SYNAPROM_FIRMWARE(self), 0x0);
32
0
  return self->product_id;
33
0
}
34
35
gboolean
36
fu_synaprom_firmware_set_signature_size(FuSynapromFirmware *self, guint32 signature_size)
37
0
{
38
0
  g_return_val_if_fail(FU_IS_SYNAPROM_FIRMWARE(self), FALSE);
39
0
  self->signature_size = signature_size;
40
0
  return TRUE;
41
0
}
42
43
static void
44
fu_synaprom_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
45
0
{
46
0
  FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware);
47
0
  fu_xmlb_builder_insert_kx(bn, "product_id", self->product_id);
48
0
}
49
50
static gboolean
51
fu_synaprom_firmware_parse(FuFirmware *firmware,
52
         GInputStream *stream,
53
         FuFirmwareParseFlags flags,
54
         GError **error)
55
394
{
56
394
  FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware);
57
394
  gsize offset = 0;
58
394
  gsize streamsz = 0;
59
60
394
  if (!fu_input_stream_size(stream, &streamsz, error))
61
0
    return FALSE;
62
394
  if (streamsz < self->signature_size + FU_STRUCT_SYNAPROM_HDR_SIZE) {
63
32
    g_set_error_literal(error,
64
32
            FWUPD_ERROR,
65
32
            FWUPD_ERROR_INVALID_DATA,
66
32
            "blob is too small to be firmware");
67
32
    return FALSE;
68
32
  }
69
362
  streamsz -= self->signature_size;
70
71
  /* parse each chunk */
72
4.95k
  while (offset < streamsz) {
73
4.80k
    guint32 hdrsz;
74
4.80k
    guint32 tag;
75
4.80k
    g_autoptr(FuFirmware) img = fu_firmware_new();
76
4.80k
    g_autoptr(FuFirmware) img_old = NULL;
77
4.80k
    g_autoptr(GByteArray) st_hdr = NULL;
78
4.80k
    g_autoptr(GInputStream) partial_stream = NULL;
79
80
    /* verify item header */
81
4.80k
    st_hdr = fu_struct_synaprom_hdr_parse_stream(stream, offset, error);
82
4.80k
    if (st_hdr == NULL)
83
0
      return FALSE;
84
4.80k
    tag = fu_struct_synaprom_hdr_get_tag(st_hdr);
85
4.80k
    if (tag >= FU_SYNAPROM_FIRMWARE_TAG_MAX) {
86
6
      g_set_error(error,
87
6
            FWUPD_ERROR,
88
6
            FWUPD_ERROR_INVALID_DATA,
89
6
            "tag 0x%04x is too large",
90
6
            tag);
91
6
      return FALSE;
92
6
    }
93
94
    /* sanity check */
95
4.79k
    img_old = fu_firmware_get_image_by_idx(firmware, tag, NULL);
96
4.79k
    if (img_old != NULL) {
97
15
      g_set_error(error,
98
15
            FWUPD_ERROR,
99
15
            FWUPD_ERROR_INVALID_DATA,
100
15
            "tag 0x%04x already present in image",
101
15
            tag);
102
15
      return FALSE;
103
15
    }
104
4.78k
    hdrsz = fu_struct_synaprom_hdr_get_bufsz(st_hdr);
105
4.78k
    if (hdrsz == 0) {
106
23
      g_set_error(error,
107
23
            FWUPD_ERROR,
108
23
            FWUPD_ERROR_INVALID_DATA,
109
23
            "empty header for tag 0x%04x",
110
23
            tag);
111
23
      return FALSE;
112
23
    }
113
4.76k
    offset += st_hdr->len;
114
4.76k
    partial_stream = fu_partial_input_stream_new(stream, offset, hdrsz, error);
115
4.76k
    if (partial_stream == NULL)
116
169
      return FALSE;
117
4.59k
    if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error))
118
0
      return FALSE;
119
4.59k
    g_debug("adding 0x%04x (%s) with size 0x%04x",
120
4.59k
      tag,
121
4.59k
      fu_synaprom_firmware_tag_to_string(tag),
122
4.59k
      hdrsz);
123
4.59k
    fu_firmware_set_idx(img, tag);
124
4.59k
    fu_firmware_set_id(img, fu_synaprom_firmware_tag_to_string(tag));
125
4.59k
    if (!fu_firmware_add_image_full(firmware, img, error))
126
1
      return FALSE;
127
128
    /* metadata */
129
4.59k
    if (tag == FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_HEADER) {
130
143
      g_autofree gchar *version = NULL;
131
143
      g_autoptr(GByteArray) st_mfw = NULL;
132
143
      st_mfw = fu_struct_synaprom_mfw_hdr_parse_stream(stream, offset, error);
133
143
      if (st_mfw == NULL)
134
0
        return FALSE;
135
143
      self->product_id = fu_struct_synaprom_mfw_hdr_get_product(st_mfw);
136
143
      version = g_strdup_printf("%u.%u",
137
143
              fu_struct_synaprom_mfw_hdr_get_vmajor(st_mfw),
138
143
              fu_struct_synaprom_mfw_hdr_get_vminor(st_mfw));
139
143
      fu_firmware_set_version(firmware, version);
140
143
    }
141
142
    /* next item */
143
4.59k
    offset += hdrsz;
144
4.59k
  }
145
148
  return TRUE;
146
362
}
147
148
static GByteArray *
149
fu_synaprom_firmware_write(FuFirmware *firmware, GError **error)
150
148
{
151
148
  FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware);
152
148
  g_autoptr(GByteArray) buf = g_byte_array_new();
153
148
  g_autoptr(GByteArray) st_hdr = fu_struct_synaprom_hdr_new();
154
148
  g_autoptr(GByteArray) st_mfw = fu_struct_synaprom_mfw_hdr_new();
155
148
  g_autoptr(GBytes) payload = NULL;
156
157
  /* add header */
158
148
  fu_struct_synaprom_hdr_set_tag(st_hdr, FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_HEADER);
159
148
  fu_struct_synaprom_hdr_set_bufsz(st_hdr, st_mfw->len);
160
148
  g_byte_array_append(buf, st_hdr->data, st_hdr->len);
161
148
  fu_struct_synaprom_mfw_hdr_set_product(st_mfw, self->product_id);
162
148
  g_byte_array_append(buf, st_mfw->data, st_mfw->len);
163
164
  /* add payload */
165
148
  payload = fu_firmware_get_bytes_with_patches(firmware, error);
166
148
  if (payload == NULL)
167
148
    return NULL;
168
0
  fu_struct_synaprom_hdr_set_tag(st_hdr, FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_PAYLOAD);
169
0
  fu_struct_synaprom_hdr_set_bufsz(st_hdr, g_bytes_get_size(payload));
170
0
  g_byte_array_append(buf, st_hdr->data, st_hdr->len);
171
0
  fu_byte_array_append_bytes(buf, payload);
172
173
  /* add signature */
174
0
  for (guint i = 0; i < self->signature_size; i++)
175
0
    fu_byte_array_append_uint8(buf, 0xff);
176
0
  return g_steal_pointer(&buf);
177
148
}
178
179
static gboolean
180
fu_synaprom_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
181
0
{
182
0
  FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware);
183
0
  guint64 tmp;
184
185
  /* simple properties */
186
0
  tmp = xb_node_query_text_as_uint(n, "product_id", NULL);
187
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
188
0
    self->product_id = tmp;
189
190
  /* success */
191
0
  return TRUE;
192
0
}
193
194
static void
195
fu_synaprom_firmware_init(FuSynapromFirmware *self)
196
394
{
197
394
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
198
394
  fu_firmware_set_images_max(FU_FIRMWARE(self), FU_SYNAPROM_FIRMWARE_COUNT_MAX);
199
394
  self->signature_size = FU_SYNAPROM_FIRMWARE_PROMETHEUS_SIGSIZE;
200
394
}
201
202
static void
203
fu_synaprom_firmware_class_init(FuSynapromFirmwareClass *klass)
204
1
{
205
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
206
1
  firmware_class->parse = fu_synaprom_firmware_parse;
207
1
  firmware_class->write = fu_synaprom_firmware_write;
208
1
  firmware_class->export = fu_synaprom_firmware_export;
209
1
  firmware_class->build = fu_synaprom_firmware_build;
210
1
}
211
212
FuFirmware *
213
fu_synaprom_firmware_new(void)
214
0
{
215
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPROM_FIRMWARE, NULL));
216
0
}