Coverage Report

Created: 2026-01-25 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/bcm57xx/fu-bcm57xx-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2018 Evan Lojewski
3
 * Copyright 2020 Richard Hughes <richard@hughsie.com>
4
 *
5
 * SPDX-License-Identifier: LGPL-2.1-or-later
6
 */
7
8
#include "config.h"
9
10
#include "fu-bcm57xx-common.h"
11
#include "fu-bcm57xx-dict-image.h"
12
#include "fu-bcm57xx-firmware.h"
13
#include "fu-bcm57xx-stage1-image.h"
14
#include "fu-bcm57xx-stage2-image.h"
15
#include "fu-bcm57xx-struct.h"
16
17
struct _FuBcm57xxFirmware {
18
  FuFirmware parent_instance;
19
  guint16 vendor;
20
  guint16 model;
21
  gboolean is_backup;
22
  guint32 phys_addr;
23
  gsize source_size;
24
  guint8 source_padchar;
25
};
26
27
2.02k
G_DEFINE_TYPE(FuBcm57xxFirmware, fu_bcm57xx_firmware, FU_TYPE_FIRMWARE)
28
2.02k
29
3.59k
#define BCM_STAGE1_HEADER_MAGIC_BROADCOM 0x0E000E03
30
2.65k
#define BCM_STAGE1_HEADER_MAGIC_MEKLORT  0x3C1D0800
31
32
2.77k
#define BCM_APE_HEADER_MAGIC 0x1A4D4342
33
34
3
#define BCM_CODE_DIRECTORY_ADDR_APE 0x07
35
36
static void
37
fu_bcm57xx_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
38
0
{
39
0
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
40
0
  fu_xmlb_builder_insert_kx(bn, "vendor", self->vendor);
41
0
  fu_xmlb_builder_insert_kx(bn, "model", self->model);
42
0
  if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) {
43
0
    fu_xmlb_builder_insert_kb(bn, "is_backup", self->is_backup);
44
0
    fu_xmlb_builder_insert_kx(bn, "phys_addr", self->phys_addr);
45
0
  }
46
0
}
47
48
static gboolean
49
fu_bcm57xx_firmware_parse_header(FuBcm57xxFirmware *self, GInputStream *stream, GError **error)
50
729
{
51
  /* verify magic and CRC */
52
729
  if (!fu_bcm57xx_verify_magic(stream, 0x0, error))
53
0
    return FALSE;
54
729
  if (!fu_bcm57xx_verify_crc(stream, error))
55
53
    return FALSE;
56
57
  /* get address */
58
676
  return fu_input_stream_read_u32(stream,
59
676
          FU_STRUCT_BCM57XX_NVRAM_HEADER_OFFSET_PHYS_ADDR,
60
676
          &self->phys_addr,
61
676
          G_BIG_ENDIAN,
62
676
          error);
63
729
}
64
65
static FuFirmware *
66
fu_bcm57xx_firmware_parse_info(FuBcm57xxFirmware *self,
67
             GInputStream *stream,
68
             FuFirmwareParseFlags flags,
69
             GError **error)
70
668
{
71
668
  guint32 mac_addr0;
72
668
  g_autoptr(FuFirmware) img = fu_firmware_new();
73
668
  g_autoptr(FuStructBcm57xxNvramInfo) st = NULL;
74
75
668
  st = fu_struct_bcm57xx_nvram_info_parse_stream(stream, 0x0, error);
76
668
  if (st == NULL)
77
0
    return NULL;
78
79
  /* if the MAC is set non-zero this is an actual backup rather than a container */
80
668
  mac_addr0 = fu_struct_bcm57xx_nvram_info_get_mac_addr(st, 0);
81
668
  self->is_backup = mac_addr0 != 0x0 && mac_addr0 != 0xffffffff;
82
83
  /* read vendor + model */
84
668
  self->vendor = fu_struct_bcm57xx_nvram_info_get_vendor(st);
85
668
  self->model = fu_struct_bcm57xx_nvram_info_get_device(st);
86
87
  /* success */
88
668
  if (!fu_firmware_parse_stream(img, stream, 0x0, flags, error))
89
0
    return NULL;
90
668
  fu_firmware_set_id(img, "info");
91
668
  return g_steal_pointer(&img);
92
668
}
93
94
static FuFirmware *
95
fu_bcm57xx_firmware_parse_stage1(FuBcm57xxFirmware *self,
96
         GInputStream *stream,
97
         guint32 *out_stage1_sz,
98
         FuFirmwareParseFlags flags,
99
         GError **error)
100
616
{
101
616
  gsize streamsz = 0;
102
616
  guint32 stage1_wrds = 0;
103
616
  guint32 stage1_sz;
104
616
  guint32 stage1_off = 0;
105
616
  g_autoptr(FuStructBcm57xxNvramHeader) st = NULL;
106
616
  g_autoptr(FuFirmware) img = fu_bcm57xx_stage1_image_new();
107
616
  g_autoptr(GInputStream) stream_tmp = NULL;
108
109
616
  if (!fu_input_stream_size(stream, &streamsz, error))
110
0
    return NULL;
111
616
  st = fu_struct_bcm57xx_nvram_header_parse_stream(stream, BCM_NVRAM_HEADER_BASE, error);
112
616
  if (st == NULL)
113
0
    return NULL;
114
616
  stage1_wrds = fu_struct_bcm57xx_nvram_header_get_size_wrds(st);
115
616
  stage1_off = fu_struct_bcm57xx_nvram_header_get_offset(st);
116
117
616
  stage1_sz = (stage1_wrds * sizeof(guint32));
118
616
  if (stage1_off != BCM_NVRAM_STAGE1_BASE) {
119
30
    g_set_error(error,
120
30
          FWUPD_ERROR,
121
30
          FWUPD_ERROR_NOT_SUPPORTED,
122
30
          "stage1 offset invalid, got: 0x%x, expected 0x%x",
123
30
          (guint)stage1_sz,
124
30
          (guint)BCM_NVRAM_STAGE1_BASE);
125
30
    return NULL;
126
30
  }
127
586
  if (stage1_off + stage1_sz > streamsz) {
128
8
    g_set_error(error,
129
8
          FWUPD_ERROR,
130
8
          FWUPD_ERROR_NOT_SUPPORTED,
131
8
          "bigger than firmware, got: 0x%x @ 0x%x",
132
8
          (guint)stage1_sz,
133
8
          (guint)stage1_off);
134
8
    return NULL;
135
8
  }
136
137
  /* verify CRC */
138
578
  stream_tmp = fu_partial_input_stream_new(stream, stage1_off, stage1_sz, error);
139
578
  if (stream_tmp == NULL)
140
15
    return NULL;
141
563
  if (!fu_firmware_parse_stream(img,
142
563
              stream_tmp,
143
563
              0x0,
144
563
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
145
563
              error))
146
105
    return NULL;
147
148
  /* needed for stage2 */
149
458
  if (out_stage1_sz != NULL)
150
458
    *out_stage1_sz = stage1_sz;
151
152
  /* success */
153
458
  fu_firmware_set_id(img, "stage1");
154
458
  fu_firmware_set_offset(img, stage1_off);
155
458
  return g_steal_pointer(&img);
156
563
}
157
158
static FuFirmware *
159
fu_bcm57xx_firmware_parse_stage2(FuBcm57xxFirmware *self,
160
         GInputStream *stream,
161
         guint32 stage1_sz,
162
         FuFirmwareParseFlags flags,
163
         GError **error)
164
458
{
165
458
  gsize streamsz = 0;
166
458
  guint32 stage2_off = 0;
167
458
  guint32 stage2_sz = 0;
168
458
  g_autoptr(FuFirmware) img = fu_bcm57xx_stage2_image_new();
169
458
  g_autoptr(GInputStream) stream_tmp = NULL;
170
171
458
  stage2_off = BCM_NVRAM_STAGE1_BASE + stage1_sz;
172
458
  if (!fu_bcm57xx_verify_magic(stream, stage2_off, error))
173
151
    return NULL;
174
307
  if (!fu_input_stream_read_u32(stream,
175
307
              stage2_off + sizeof(guint32),
176
307
              &stage2_sz,
177
307
              G_BIG_ENDIAN,
178
307
              error))
179
1
    return NULL;
180
306
  if (!fu_input_stream_size(stream, &streamsz, error))
181
0
    return NULL;
182
306
  if (stage2_off + stage2_sz > streamsz) {
183
25
    g_set_error(error,
184
25
          FWUPD_ERROR,
185
25
          FWUPD_ERROR_NOT_SUPPORTED,
186
25
          "bigger than firmware, got: 0x%x @ 0x%x",
187
25
          (guint)stage2_sz,
188
25
          (guint)stage2_off);
189
25
    return NULL;
190
25
  }
191
192
  /* verify CRC */
193
281
  stream_tmp = fu_partial_input_stream_new(stream, stage2_off + 0x8, stage2_sz, error);
194
281
  if (stream_tmp == NULL)
195
29
    return NULL;
196
252
  if (!fu_firmware_parse_stream(img,
197
252
              stream_tmp,
198
252
              0x0,
199
252
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
200
252
              error))
201
4
    return NULL;
202
203
  /* success */
204
248
  fu_firmware_set_id(img, "stage2");
205
248
  fu_firmware_set_offset(img, stage2_off);
206
248
  return g_steal_pointer(&img);
207
252
}
208
209
static gboolean
210
fu_bcm57xx_firmware_parse_dict(FuBcm57xxFirmware *self,
211
             GInputStream *stream,
212
             guint idx,
213
             FuFirmwareParseFlags flags,
214
             GError **error)
215
1.65k
{
216
1.65k
  gsize streamsz = 0;
217
1.65k
  guint32 dict_addr = 0x0;
218
1.65k
  guint32 dict_info = 0x0;
219
1.65k
  guint32 dict_off = 0x0;
220
1.65k
  guint32 dict_sz;
221
1.65k
  guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * FU_STRUCT_BCM57XX_NVRAM_DIRECTORY_SIZE);
222
1.65k
  g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new();
223
1.65k
  g_autoptr(FuStructBcm57xxNvramDirectory) st = NULL;
224
1.65k
  g_autoptr(GInputStream) stream_tmp = NULL;
225
226
  /* header */
227
1.65k
  st = fu_struct_bcm57xx_nvram_directory_parse_stream(stream, base, error);
228
1.65k
  if (st == NULL)
229
0
    return FALSE;
230
1.65k
  dict_addr = fu_struct_bcm57xx_nvram_directory_get_addr(st);
231
1.65k
  dict_info = fu_struct_bcm57xx_nvram_directory_get_size_wrds(st);
232
1.65k
  dict_off = fu_struct_bcm57xx_nvram_directory_get_offset(st);
233
234
  /* no dict stored */
235
1.65k
  if (dict_addr == 0 && dict_info == 0 && dict_off == 0)
236
234
    return TRUE;
237
238
1.42k
  dict_sz =
239
1.42k
      (dict_info & 0x00FFFFFF) * sizeof(guint32); /* implies that maximum size is 16 MB */
240
1.42k
  fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img),
241
1.42k
           (dict_info & 0x0F000000) >> 24);
242
1.42k
  fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), (dict_info & 0xF0000000) >> 28);
243
1.42k
  fu_firmware_set_addr(img, dict_addr);
244
1.42k
  fu_firmware_set_offset(img, dict_off);
245
1.42k
  fu_firmware_set_idx(img, 0x80 + idx);
246
247
  /* empty */
248
1.42k
  if (dict_sz == 0) {
249
687
    g_autoptr(GBytes) blob = g_bytes_new(NULL, 0);
250
687
    fu_firmware_set_bytes(img, blob);
251
687
    return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
252
687
  }
253
254
  /* check against image size */
255
737
  if (!fu_input_stream_size(stream, &streamsz, error))
256
0
    return FALSE;
257
737
  if (dict_off + dict_sz > streamsz) {
258
86
    g_set_error(error,
259
86
          FWUPD_ERROR,
260
86
          FWUPD_ERROR_NOT_SUPPORTED,
261
86
          "bigger than firmware, got: 0x%x @ 0x%x",
262
86
          (guint)dict_sz,
263
86
          (guint)dict_off);
264
86
    return FALSE;
265
86
  }
266
651
  stream_tmp = fu_partial_input_stream_new(stream, dict_off, dict_sz, error);
267
651
  if (stream_tmp == NULL)
268
35
    return FALSE;
269
616
  if (!fu_firmware_parse_stream(img,
270
616
              stream_tmp,
271
616
              0x0,
272
616
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
273
616
              error))
274
0
    return FALSE;
275
276
  /* success */
277
616
  return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
278
616
}
279
280
static gboolean
281
fu_bcm57xx_firmware_validate(FuFirmware *firmware,
282
           GInputStream *stream,
283
           gsize offset,
284
           GError **error)
285
984
{
286
984
  guint32 magic = 0;
287
288
984
  if (!fu_input_stream_read_u32(stream, 0x0, &magic, G_BIG_ENDIAN, error)) {
289
7
    g_prefix_error_literal(error, "failed to read magic: ");
290
7
    return FALSE;
291
7
  }
292
977
  if (magic != BCM_APE_HEADER_MAGIC && magic != BCM_STAGE1_HEADER_MAGIC_BROADCOM &&
293
914
      magic != BCM_STAGE1_HEADER_MAGIC_MEKLORT && magic != BCM_NVRAM_MAGIC) {
294
152
    g_set_error(error,
295
152
          FWUPD_ERROR,
296
152
          FWUPD_ERROR_INVALID_FILE,
297
152
          "file not supported, got: 0x%08X",
298
152
          magic);
299
152
    return FALSE;
300
152
  }
301
302
  /* success */
303
825
  return TRUE;
304
977
}
305
306
static gboolean
307
fu_bcm57xx_firmware_parse(FuFirmware *firmware,
308
        GInputStream *stream,
309
        FuFirmwareParseFlags flags,
310
        GError **error)
311
825
{
312
825
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
313
825
  gsize streamsz = 0;
314
825
  guint32 magic = 0;
315
825
  guint32 stage1_sz = 0;
316
825
  g_autoptr(FuFirmware) img_info2 = fu_firmware_new();
317
825
  g_autoptr(FuFirmware) img_info = NULL;
318
825
  g_autoptr(FuFirmware) img_stage1 = NULL;
319
825
  g_autoptr(FuFirmware) img_stage2 = NULL;
320
825
  g_autoptr(FuFirmware) img_vpd = fu_firmware_new();
321
825
  g_autoptr(GInputStream) stream_header = NULL;
322
825
  g_autoptr(GInputStream) stream_info2 = NULL;
323
825
  g_autoptr(GInputStream) stream_info = NULL;
324
825
  g_autoptr(GInputStream) stream_vpd = NULL;
325
326
  /* try to autodetect the file type */
327
825
  if (!fu_input_stream_read_u32(stream, 0x0, &magic, G_BIG_ENDIAN, error))
328
0
    return FALSE;
329
330
  /* standalone APE */
331
825
  if (magic == BCM_APE_HEADER_MAGIC) {
332
3
    g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new();
333
3
    fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img), 0xD);
334
3
    fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), 0x0);
335
3
    fu_firmware_set_addr(img, BCM_CODE_DIRECTORY_ADDR_APE);
336
3
    fu_firmware_set_id(img, "ape");
337
3
    return fu_firmware_add_image(firmware, img, error);
338
3
  }
339
340
  /* standalone stage1 */
341
822
  if (magic == BCM_STAGE1_HEADER_MAGIC_BROADCOM || magic == BCM_STAGE1_HEADER_MAGIC_MEKLORT) {
342
83
    g_autoptr(FuFirmware) img_stage1_standalone = fu_firmware_new();
343
83
    if (!fu_firmware_set_stream(img_stage1_standalone, stream, error))
344
0
      return FALSE;
345
83
    fu_firmware_set_id(img_stage1_standalone, "stage1");
346
83
    return fu_firmware_add_image(firmware, img_stage1_standalone, error);
347
83
  }
348
349
  /* not full NVRAM image */
350
739
  if (magic != BCM_NVRAM_MAGIC) {
351
0
    g_set_error(error,
352
0
          FWUPD_ERROR,
353
0
          FWUPD_ERROR_NOT_SUPPORTED,
354
0
          "file not supported, got: 0x%08X",
355
0
          magic);
356
0
    return FALSE;
357
0
  }
358
359
  /* save the size so we can export the padding for a perfect roundtrip */
360
739
  if (!fu_input_stream_size(stream, &streamsz, error))
361
0
    return FALSE;
362
739
  self->source_size = streamsz;
363
739
  if (!fu_input_stream_read_u8(stream, streamsz - 1, &self->source_padchar, error))
364
0
    return FALSE;
365
366
  /* NVRAM header */
367
739
  stream_header = fu_partial_input_stream_new(stream,
368
739
                BCM_NVRAM_HEADER_BASE,
369
739
                FU_STRUCT_BCM57XX_NVRAM_HEADER_SIZE,
370
739
                error);
371
739
  if (stream_header == NULL)
372
10
    return FALSE;
373
729
  if (!fu_bcm57xx_firmware_parse_header(self, stream_header, error)) {
374
53
    g_prefix_error_literal(error, "failed to parse header: ");
375
53
    return FALSE;
376
53
  }
377
378
  /* info */
379
676
  stream_info = fu_partial_input_stream_new(stream,
380
676
              BCM_NVRAM_INFO_BASE,
381
676
              FU_STRUCT_BCM57XX_NVRAM_INFO_SIZE,
382
676
              error);
383
676
  if (stream_info == NULL)
384
8
    return FALSE;
385
668
  img_info = fu_bcm57xx_firmware_parse_info(self, stream_info, flags, error);
386
668
  if (img_info == NULL) {
387
0
    g_prefix_error_literal(error, "failed to parse info: ");
388
0
    return FALSE;
389
0
  }
390
668
  fu_firmware_set_offset(img_info, BCM_NVRAM_INFO_BASE);
391
668
  if (!fu_firmware_add_image(firmware, img_info, error))
392
0
    return FALSE;
393
394
  /* VPD */
395
668
  stream_vpd =
396
668
      fu_partial_input_stream_new(stream, BCM_NVRAM_VPD_BASE, BCM_NVRAM_VPD_SZ, error);
397
668
  if (stream_vpd == NULL)
398
47
    return FALSE;
399
621
  if (!fu_firmware_parse_stream(img_vpd, stream_vpd, 0x0, flags, error)) {
400
0
    g_prefix_error_literal(error, "failed to parse VPD: ");
401
0
    return FALSE;
402
0
  }
403
621
  fu_firmware_set_id(img_vpd, "vpd");
404
621
  fu_firmware_set_offset(img_vpd, BCM_NVRAM_VPD_BASE);
405
621
  if (!fu_firmware_add_image(firmware, img_vpd, error))
406
0
    return FALSE;
407
408
  /* info2 */
409
621
  stream_info2 =
410
621
      fu_partial_input_stream_new(stream, BCM_NVRAM_INFO2_BASE, BCM_NVRAM_INFO2_SZ, error);
411
621
  if (stream_info2 == NULL)
412
5
    return FALSE;
413
616
  if (!fu_firmware_parse_stream(img_info2, stream_info2, 0x0, flags, error)) {
414
0
    g_prefix_error_literal(error, "failed to parse info2: ");
415
0
    return FALSE;
416
0
  }
417
616
  fu_firmware_set_id(img_info2, "info2");
418
616
  fu_firmware_set_offset(img_info2, BCM_NVRAM_INFO2_BASE);
419
616
  if (!fu_firmware_add_image(firmware, img_info2, error))
420
0
    return FALSE;
421
422
  /* stage1 */
423
616
  img_stage1 = fu_bcm57xx_firmware_parse_stage1(self, stream, &stage1_sz, flags, error);
424
616
  if (img_stage1 == NULL) {
425
158
    g_prefix_error_literal(error, "failed to parse stage1: ");
426
158
    return FALSE;
427
158
  }
428
458
  if (!fu_firmware_add_image(firmware, img_stage1, error))
429
0
    return FALSE;
430
431
  /* stage2 */
432
458
  img_stage2 = fu_bcm57xx_firmware_parse_stage2(self, stream, stage1_sz, flags, error);
433
458
  if (img_stage2 == NULL) {
434
210
    g_prefix_error_literal(error, "failed to parse stage2: ");
435
210
    return FALSE;
436
210
  }
437
248
  if (!fu_firmware_add_image(firmware, img_stage2, error))
438
0
    return FALSE;
439
440
  /* dictionaries, e.g. APE */
441
1.78k
  for (guint i = 0; i < 8; i++) {
442
1.65k
    if (!fu_bcm57xx_firmware_parse_dict(self, stream, i, flags, error)) {
443
121
      g_prefix_error(error, "failed to parse dict 0x%x: ", i);
444
121
      return FALSE;
445
121
    }
446
1.65k
  }
447
448
  /* success */
449
127
  return TRUE;
450
248
}
451
452
static GBytes *
453
_g_bytes_new_sized(gsize sz)
454
0
{
455
0
  g_autoptr(GByteArray) tmp = g_byte_array_sized_new(sz);
456
0
  for (gsize i = 0; i < sz; i++)
457
0
    fu_byte_array_append_uint8(tmp, 0x0);
458
0
  return g_bytes_new(tmp->data, tmp->len);
459
0
}
460
461
static gboolean
462
fu_bcm57xx_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
463
0
{
464
0
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
465
0
  guint64 tmp;
466
467
  /* two simple properties */
468
0
  tmp = xb_node_query_text_as_uint(n, "vendor", NULL);
469
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
470
0
    self->vendor = tmp;
471
0
  tmp = xb_node_query_text_as_uint(n, "model", NULL);
472
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT16)
473
0
    self->model = tmp;
474
475
  /* success */
476
0
  return TRUE;
477
0
}
478
479
static GByteArray *
480
fu_bcm57xx_firmware_write(FuFirmware *firmware, GError **error)
481
213
{
482
213
  gsize off = BCM_NVRAM_STAGE1_BASE;
483
213
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
484
213
  g_autoptr(GByteArray) buf = g_byte_array_sized_new(self->source_size);
485
213
  g_autoptr(FuFirmware) img_info2 = NULL;
486
213
  g_autoptr(FuFirmware) img_info = NULL;
487
213
  g_autoptr(FuFirmware) img_stage1 = NULL;
488
213
  g_autoptr(FuFirmware) img_stage2 = NULL;
489
213
  g_autoptr(FuFirmware) img_vpd = NULL;
490
213
  g_autoptr(GBytes) blob_info2 = NULL;
491
213
  g_autoptr(GBytes) blob_info = NULL;
492
213
  g_autoptr(GBytes) blob_stage1 = NULL;
493
213
  g_autoptr(GBytes) blob_stage2 = NULL;
494
213
  g_autoptr(GBytes) blob_vpd = NULL;
495
213
  g_autoptr(GPtrArray) blob_dicts = NULL;
496
497
  /* write out the things we need to pre-compute */
498
213
  img_stage1 = fu_firmware_get_image_by_id(firmware, "stage1", error);
499
213
  if (img_stage1 == NULL)
500
3
    return NULL;
501
210
  blob_stage1 = fu_firmware_write(img_stage1, error);
502
210
  if (blob_stage1 == NULL)
503
32
    return NULL;
504
178
  off += g_bytes_get_size(blob_stage1);
505
178
  img_stage2 = fu_firmware_get_image_by_id(firmware, "stage2", error);
506
178
  if (img_stage2 == NULL)
507
83
    return NULL;
508
95
  blob_stage2 = fu_firmware_write(img_stage2, error);
509
95
  if (blob_stage2 == NULL)
510
8
    return NULL;
511
87
  off += g_bytes_get_size(blob_stage2);
512
513
  /* add header */
514
87
  fu_byte_array_append_uint32(buf, BCM_NVRAM_MAGIC, G_BIG_ENDIAN);
515
87
  fu_byte_array_append_uint32(buf, self->phys_addr, G_BIG_ENDIAN);
516
87
  fu_byte_array_append_uint32(buf,
517
87
            g_bytes_get_size(blob_stage1) / sizeof(guint32),
518
87
            G_BIG_ENDIAN);
519
87
  fu_byte_array_append_uint32(buf, BCM_NVRAM_STAGE1_BASE, G_BIG_ENDIAN);
520
87
  fu_byte_array_append_uint32(buf,
521
87
            fu_crc32(FU_CRC_KIND_B32_STANDARD, buf->data, buf->len),
522
87
            G_LITTLE_ENDIAN);
523
524
  /* add directory entries */
525
87
  blob_dicts = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref);
526
667
  for (guint i = 0; i < 8; i++) {
527
606
    g_autoptr(FuFirmware) img = NULL;
528
606
    g_autoptr(GBytes) blob = NULL;
529
530
606
    img = fu_firmware_get_image_by_idx(firmware, 0x80 + i, NULL);
531
606
    if (img != NULL) {
532
427
      blob = fu_firmware_write(img, error);
533
427
      if (blob == NULL)
534
26
        return NULL;
535
427
    }
536
580
    if (blob != NULL) {
537
401
      fu_byte_array_append_uint32(buf, fu_firmware_get_addr(img), G_BIG_ENDIAN);
538
401
      fu_byte_array_append_uint32(
539
401
          buf,
540
401
          (g_bytes_get_size(blob) / sizeof(guint32)) |
541
401
        (guint32)fu_bcm57xx_dict_image_get_target(
542
401
            FU_BCM57XX_DICT_IMAGE(img))
543
401
            << 24 |
544
401
        (guint32)fu_bcm57xx_dict_image_get_kind(FU_BCM57XX_DICT_IMAGE(img))
545
401
            << 28,
546
401
          G_BIG_ENDIAN);
547
401
      if (g_bytes_get_size(blob) > 0) {
548
401
        fu_byte_array_append_uint32(buf, off, G_BIG_ENDIAN);
549
401
        off += g_bytes_get_size(blob);
550
401
      } else {
551
0
        fu_byte_array_append_uint32(buf, 0x0, G_BIG_ENDIAN);
552
0
      }
553
401
    } else {
554
179
      blob = g_bytes_new(NULL, 0);
555
2.32k
      for (guint32 j = 0; j < sizeof(guint32) * 3; j++)
556
2.14k
        fu_byte_array_append_uint8(buf, 0x0);
557
179
    }
558
580
    g_ptr_array_add(blob_dicts, g_steal_pointer(&blob));
559
580
  }
560
561
  /* add info */
562
61
  img_info = fu_firmware_get_image_by_id(firmware, "info", NULL);
563
61
  if (img_info != NULL) {
564
61
    blob_info = fu_firmware_write(img_info, error);
565
61
    if (blob_info == NULL)
566
61
      return NULL;
567
61
  } else {
568
0
    g_autoptr(FuStructBcm57xxNvramInfo) st = fu_struct_bcm57xx_nvram_info_new();
569
0
    fu_struct_bcm57xx_nvram_info_set_device(st, self->model);
570
0
    fu_struct_bcm57xx_nvram_info_set_vendor(st, self->vendor);
571
0
    blob_info = fu_struct_bcm57xx_nvram_info_to_bytes(st);
572
0
  }
573
0
  fu_byte_array_append_bytes(buf, blob_info);
574
575
  /* add vpd */
576
0
  img_vpd = fu_firmware_get_image_by_id(firmware, "vpd", NULL);
577
0
  if (img_vpd != NULL) {
578
0
    blob_vpd = fu_firmware_write(img_vpd, error);
579
0
    if (blob_vpd == NULL)
580
0
      return NULL;
581
0
  } else {
582
0
    blob_vpd = _g_bytes_new_sized(BCM_NVRAM_VPD_SZ);
583
0
  }
584
0
  fu_byte_array_append_bytes(buf, blob_vpd);
585
586
  /* add info2 */
587
0
  img_info2 = fu_firmware_get_image_by_id(firmware, "info2", NULL);
588
0
  if (img_info2 != NULL) {
589
0
    blob_info2 = fu_firmware_write(img_info2, error);
590
0
    if (blob_info2 == NULL)
591
0
      return NULL;
592
0
  } else {
593
0
    blob_info2 = _g_bytes_new_sized(BCM_NVRAM_INFO2_SZ);
594
0
  }
595
0
  fu_byte_array_append_bytes(buf, blob_info2);
596
597
  /* add stage1+2 */
598
0
  fu_byte_array_append_bytes(buf, blob_stage1);
599
0
  fu_byte_array_append_bytes(buf, blob_stage2);
600
601
  /* add dictionaries, e.g. APE */
602
0
  for (guint i = 0; i < blob_dicts->len; i++) {
603
0
    GBytes *blob = g_ptr_array_index(blob_dicts, i);
604
0
    fu_byte_array_append_bytes(buf, blob);
605
0
  }
606
607
  /* pad until full */
608
0
  for (guint32 i = buf->len; i < self->source_size; i++)
609
0
    fu_byte_array_append_uint8(buf, self->source_padchar);
610
611
  /* add EOF */
612
0
  return g_steal_pointer(&buf);
613
0
}
614
615
guint16
616
fu_bcm57xx_firmware_get_vendor(FuBcm57xxFirmware *self)
617
0
{
618
0
  return self->vendor;
619
0
}
620
621
guint16
622
fu_bcm57xx_firmware_get_model(FuBcm57xxFirmware *self)
623
0
{
624
0
  return self->model;
625
0
}
626
627
static void
628
fu_bcm57xx_firmware_init(FuBcm57xxFirmware *self)
629
984
{
630
984
  self->phys_addr = BCM_PHYS_ADDR_DEFAULT;
631
984
  self->source_size = BCM_FIRMWARE_SIZE;
632
984
  self->source_padchar = 0xff;
633
984
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID);
634
984
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
635
984
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
636
984
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
637
984
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_BCM57XX_STAGE1_IMAGE);
638
984
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_BCM57XX_STAGE2_IMAGE);
639
984
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_BCM57XX_DICT_IMAGE);
640
984
}
641
642
static void
643
fu_bcm57xx_firmware_class_init(FuBcm57xxFirmwareClass *klass)
644
1
{
645
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
646
1
  firmware_class->validate = fu_bcm57xx_firmware_validate;
647
1
  firmware_class->parse = fu_bcm57xx_firmware_parse;
648
1
  firmware_class->export = fu_bcm57xx_firmware_export;
649
1
  firmware_class->write = fu_bcm57xx_firmware_write;
650
1
  firmware_class->build = fu_bcm57xx_firmware_build;
651
1
}
652
653
FuFirmware *
654
fu_bcm57xx_firmware_new(void)
655
0
{
656
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_BCM57XX_FIRMWARE, NULL));
657
0
}