Coverage Report

Created: 2025-07-18 06:26

/src/fwupd/libfwupdplugin/fu-dfuse-firmware.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2015 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-dfu-firmware-private.h"
13
#include "fu-dfu-firmware-struct.h"
14
#include "fu-dfuse-firmware.h"
15
#include "fu-input-stream.h"
16
17
/**
18
 * FuDfuseFirmware:
19
 *
20
 * A DfuSe firmware image.
21
 *
22
 * See also: [class@FuDfuFirmware]
23
 */
24
25
G_DEFINE_TYPE(FuDfuseFirmware, fu_dfuse_firmware, FU_TYPE_DFU_FIRMWARE)
26
27
static FuChunk *
28
fu_dfuse_firmware_image_chunk_parse(FuDfuseFirmware *self,
29
            GInputStream *stream,
30
            gsize *offset,
31
            GError **error)
32
1.57k
{
33
1.57k
  g_autoptr(FuChunk) chk = NULL;
34
1.57k
  g_autoptr(GByteArray) st_ele = NULL;
35
1.57k
  g_autoptr(GBytes) blob = NULL;
36
37
  /* create new chunk */
38
1.57k
  st_ele = fu_struct_dfuse_element_parse_stream(stream, *offset, error);
39
1.57k
  if (st_ele == NULL)
40
148
    return NULL;
41
1.43k
  *offset += st_ele->len;
42
1.43k
  blob = fu_input_stream_read_bytes(stream,
43
1.43k
            *offset,
44
1.43k
            fu_struct_dfuse_element_get_size(st_ele),
45
1.43k
            NULL,
46
1.43k
            error);
47
1.43k
  if (blob == NULL)
48
38
    return NULL;
49
1.39k
  chk = fu_chunk_bytes_new(blob);
50
1.39k
  fu_chunk_set_address(chk, fu_struct_dfuse_element_get_address(st_ele));
51
1.39k
  *offset += fu_chunk_get_data_sz(chk);
52
53
  /* success */
54
1.39k
  return g_steal_pointer(&chk);
55
1.43k
}
56
57
static FuFirmware *
58
fu_dfuse_firmware_image_parse_stream(FuDfuseFirmware *self,
59
             GInputStream *stream,
60
             gsize *offset,
61
             GError **error)
62
385
{
63
385
  guint chunks;
64
385
  g_autoptr(FuFirmware) image = fu_firmware_new();
65
385
  g_autoptr(GByteArray) st_img = NULL;
66
67
  /* verify image signature */
68
385
  st_img = fu_struct_dfuse_image_parse_stream(stream, *offset, error);
69
385
  if (st_img == NULL)
70
110
    return NULL;
71
72
  /* set properties */
73
275
  fu_firmware_set_idx(image, fu_struct_dfuse_image_get_alt_setting(st_img));
74
275
  if (fu_struct_dfuse_image_get_target_named(st_img) == 0x01) {
75
70
    g_autofree gchar *target_name = fu_struct_dfuse_image_get_target_name(st_img);
76
70
    fu_firmware_set_id(image, target_name);
77
70
  }
78
79
  /* no chunks */
80
275
  chunks = fu_struct_dfuse_image_get_chunks(st_img);
81
275
  if (chunks == 0) {
82
3
    g_set_error_literal(error,
83
3
            FWUPD_ERROR,
84
3
            FWUPD_ERROR_INVALID_FILE,
85
3
            "DfuSe image has no chunks");
86
3
    return NULL;
87
3
  }
88
89
  /* parse chunks */
90
272
  *offset += st_img->len;
91
1.66k
  for (guint j = 0; j < chunks; j++) {
92
1.57k
    g_autoptr(FuChunk) chk = NULL;
93
1.57k
    chk = fu_dfuse_firmware_image_chunk_parse(self, stream, offset, error);
94
1.57k
    if (chk == NULL)
95
186
      return NULL;
96
1.39k
    fu_firmware_add_chunk(image, chk);
97
1.39k
  }
98
99
  /* success */
100
86
  return g_steal_pointer(&image);
101
272
}
102
103
static gboolean
104
fu_dfuse_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
105
5.47M
{
106
5.47M
  return fu_struct_dfuse_hdr_validate_stream(stream, offset, error);
107
5.47M
}
108
109
static gboolean
110
fu_dfuse_firmware_parse(FuFirmware *firmware,
111
      GInputStream *stream,
112
      FuFirmwareParseFlags flags,
113
      GError **error)
114
1.22k
{
115
1.22k
  FuDfuFirmware *dfu_firmware = FU_DFU_FIRMWARE(firmware);
116
1.22k
  gsize offset = 0;
117
1.22k
  gsize streamsz = 0;
118
1.22k
  guint8 targets = 0;
119
1.22k
  g_autoptr(GByteArray) st_hdr = NULL;
120
121
  /* DFU footer first */
122
1.22k
  if (!fu_dfu_firmware_parse_footer(dfu_firmware, stream, flags, error))
123
680
    return FALSE;
124
125
  /* parse */
126
549
  st_hdr = fu_struct_dfuse_hdr_parse_stream(stream, offset, error);
127
549
  if (st_hdr == NULL)
128
0
    return FALSE;
129
130
  /* check image size */
131
549
  if (!fu_input_stream_size(stream, &streamsz, error))
132
0
    return FALSE;
133
549
  if (fu_struct_dfuse_hdr_get_image_size(st_hdr) !=
134
549
      streamsz - fu_dfu_firmware_get_footer_len(dfu_firmware)) {
135
164
    g_set_error(error,
136
164
          FWUPD_ERROR,
137
164
          FWUPD_ERROR_INTERNAL,
138
164
          "invalid DfuSe image size, "
139
164
          "got %" G_GUINT32_FORMAT ", "
140
164
          "expected %" G_GSIZE_FORMAT,
141
164
          fu_struct_dfuse_hdr_get_image_size(st_hdr),
142
164
          streamsz - fu_dfu_firmware_get_footer_len(dfu_firmware));
143
164
    return FALSE;
144
164
  }
145
146
  /* parse the image targets */
147
385
  targets = fu_struct_dfuse_hdr_get_targets(st_hdr);
148
385
  offset += st_hdr->len;
149
471
  for (guint i = 0; i < targets; i++) {
150
385
    g_autoptr(FuFirmware) image = NULL;
151
385
    image = fu_dfuse_firmware_image_parse_stream(FU_DFUSE_FIRMWARE(firmware),
152
385
                   stream,
153
385
                   &offset,
154
385
                   error);
155
385
    if (image == NULL)
156
299
      return FALSE;
157
86
    if (!fu_firmware_add_image_full(firmware, image, error))
158
0
      return FALSE;
159
86
  }
160
86
  return TRUE;
161
385
}
162
163
static GBytes *
164
fu_dfuse_firmware_chunk_write(FuDfuseFirmware *self, FuChunk *chk)
165
503
{
166
503
  g_autoptr(GByteArray) st_ele = fu_struct_dfuse_element_new();
167
503
  fu_struct_dfuse_element_set_address(st_ele, fu_chunk_get_address(chk));
168
503
  fu_struct_dfuse_element_set_size(st_ele, fu_chunk_get_data_sz(chk));
169
503
  g_byte_array_append(st_ele, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk));
170
503
  return g_bytes_new(st_ele->data, st_ele->len);
171
503
}
172
173
static GBytes *
174
fu_dfuse_firmware_write_image(FuDfuseFirmware *self, FuFirmware *image, GError **error)
175
61
{
176
61
  gsize totalsz = 0;
177
61
  g_autoptr(GByteArray) st_img = fu_struct_dfuse_image_new();
178
61
  g_autoptr(GPtrArray) blobs = NULL;
179
61
  g_autoptr(GPtrArray) chunks = NULL;
180
181
  /* get total size */
182
61
  blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref);
183
61
  chunks = fu_firmware_get_chunks(image, error);
184
61
  if (chunks == NULL)
185
0
    return NULL;
186
564
  for (guint i = 0; i < chunks->len; i++) {
187
503
    FuChunk *chk = g_ptr_array_index(chunks, i);
188
503
    GBytes *bytes = fu_dfuse_firmware_chunk_write(self, chk);
189
503
    g_ptr_array_add(blobs, bytes);
190
503
    totalsz += g_bytes_get_size(bytes);
191
503
  }
192
193
  /* add prefix */
194
61
  fu_struct_dfuse_image_set_alt_setting(st_img, fu_firmware_get_idx(image));
195
61
  if (fu_firmware_get_id(image) != NULL) {
196
23
    fu_struct_dfuse_image_set_target_named(st_img, 0x01);
197
23
    if (!fu_struct_dfuse_image_set_target_name(st_img,
198
23
                 fu_firmware_get_id(image),
199
23
                 error))
200
0
      return NULL;
201
23
  }
202
61
  fu_struct_dfuse_image_set_target_size(st_img, totalsz);
203
61
  fu_struct_dfuse_image_set_chunks(st_img, chunks->len);
204
205
  /* copy data */
206
564
  for (guint i = 0; i < blobs->len; i++) {
207
503
    GBytes *blob = g_ptr_array_index(blobs, i);
208
503
    fu_byte_array_append_bytes(st_img, blob);
209
503
  }
210
61
  return g_bytes_new(st_img->data, st_img->len);
211
61
}
212
213
static GByteArray *
214
fu_dfuse_firmware_write(FuFirmware *firmware, GError **error)
215
86
{
216
86
  FuDfuseFirmware *self = FU_DFUSE_FIRMWARE(firmware);
217
86
  gsize totalsz = 0;
218
86
  g_autoptr(GByteArray) st_hdr = fu_struct_dfuse_hdr_new();
219
86
  g_autoptr(GBytes) blob_noftr = NULL;
220
86
  g_autoptr(GPtrArray) blobs = NULL;
221
86
  g_autoptr(GPtrArray) images = NULL;
222
223
  /* create mutable output buffer */
224
86
  blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref);
225
86
  images = fu_firmware_get_images(FU_FIRMWARE(firmware));
226
147
  for (guint i = 0; i < images->len; i++) {
227
61
    FuFirmware *img = g_ptr_array_index(images, i);
228
61
    g_autoptr(GBytes) blob = NULL;
229
61
    blob = fu_dfuse_firmware_write_image(self, img, error);
230
61
    if (blob == NULL)
231
0
      return NULL;
232
61
    totalsz += g_bytes_get_size(blob);
233
61
    g_ptr_array_add(blobs, g_steal_pointer(&blob));
234
61
  }
235
236
  /* DfuSe header */
237
86
  fu_struct_dfuse_hdr_set_image_size(st_hdr, st_hdr->len + totalsz);
238
86
  if (images->len > G_MAXUINT8) {
239
0
    g_set_error(error,
240
0
          FWUPD_ERROR,
241
0
          FWUPD_ERROR_INTERNAL,
242
0
          "too many (%u) images to write DfuSe file",
243
0
          images->len);
244
0
    return NULL;
245
0
  }
246
86
  fu_struct_dfuse_hdr_set_targets(st_hdr, (guint8)images->len);
247
248
  /* copy images */
249
147
  for (guint i = 0; i < blobs->len; i++) {
250
61
    GBytes *blob = g_ptr_array_index(blobs, i);
251
61
    fu_byte_array_append_bytes(st_hdr, blob);
252
61
  }
253
254
  /* return blob */
255
86
  blob_noftr = g_bytes_new(st_hdr->data, st_hdr->len);
256
86
  return fu_dfu_firmware_append_footer(FU_DFU_FIRMWARE(firmware), blob_noftr, error);
257
86
}
258
259
static void
260
fu_dfuse_firmware_init(FuDfuseFirmware *self)
261
1.76k
{
262
1.76k
  fu_dfu_firmware_set_version(FU_DFU_FIRMWARE(self), FU_DFU_FIRMARE_VERSION_DFUSE);
263
1.76k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 255);
264
1.76k
}
265
266
static void
267
fu_dfuse_firmware_class_init(FuDfuseFirmwareClass *klass)
268
1
{
269
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
270
1
  firmware_class->validate = fu_dfuse_firmware_validate;
271
1
  firmware_class->parse = fu_dfuse_firmware_parse;
272
1
  firmware_class->write = fu_dfuse_firmware_write;
273
1
}
274
275
/**
276
 * fu_dfuse_firmware_new:
277
 *
278
 * Creates a new #FuFirmware of sub type DfuSe
279
 *
280
 * Since: 1.5.6
281
 **/
282
FuFirmware *
283
fu_dfuse_firmware_new(void)
284
0
{
285
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_DFUSE_FIRMWARE, NULL));
286
0
}