Coverage Report

Created: 2026-06-15 06:54

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