Coverage Report

Created: 2026-06-10 07:04

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.06k
G_DEFINE_TYPE(FuBcm57xxFirmware, fu_bcm57xx_firmware, FU_TYPE_FIRMWARE)
28
2.06k
29
3.63k
#define BCM_STAGE1_HEADER_MAGIC_BROADCOM 0x0E000E03
30
2.70k
#define BCM_STAGE1_HEADER_MAGIC_MEKLORT  0x3C1D0800
31
32
2.80k
#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
741
{
51
  /* verify magic and CRC */
52
741
  if (!fu_bcm57xx_verify_magic(stream, 0x0, error))
53
0
    return FALSE;
54
741
  if (!fu_bcm57xx_verify_crc(stream, error))
55
50
    return FALSE;
56
57
  /* get address */
58
691
  return fu_input_stream_read_u32(stream,
59
691
          FU_STRUCT_BCM57XX_NVRAM_HEADER_OFFSET_PHYS_ADDR,
60
691
          &self->phys_addr,
61
691
          G_BIG_ENDIAN,
62
691
          error);
63
741
}
64
65
static FuFirmware *
66
fu_bcm57xx_firmware_parse_info(FuBcm57xxFirmware *self,
67
             GInputStream *stream,
68
             FuFirmwareParseFlags flags,
69
             GError **error)
70
684
{
71
684
  guint32 mac_addr0;
72
684
  g_autoptr(FuFirmware) img = fu_firmware_new();
73
684
  g_autoptr(FuStructBcm57xxNvramInfo) st = NULL;
74
75
684
  st = fu_struct_bcm57xx_nvram_info_parse_stream(stream, 0x0, error);
76
684
  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
684
  mac_addr0 = fu_struct_bcm57xx_nvram_info_get_mac_addr(st, 0);
81
684
  self->is_backup = mac_addr0 != 0x0 && mac_addr0 != 0xffffffff;
82
83
  /* read vendor + model */
84
684
  self->vendor = fu_struct_bcm57xx_nvram_info_get_vendor(st);
85
684
  self->model = fu_struct_bcm57xx_nvram_info_get_device(st);
86
87
  /* success */
88
684
  if (!fu_firmware_parse_stream(img, stream, 0x0, flags, error))
89
0
    return NULL;
90
684
  fu_firmware_set_id(img, "info");
91
684
  return g_steal_pointer(&img);
92
684
}
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
631
{
101
631
  gsize streamsz = 0;
102
631
  guint32 stage1_wrds = 0;
103
631
  guint32 stage1_sz;
104
631
  guint32 stage1_off = 0;
105
631
  g_autoptr(FuStructBcm57xxNvramHeader) st = NULL;
106
631
  g_autoptr(FuFirmware) img = fu_bcm57xx_stage1_image_new();
107
631
  g_autoptr(GInputStream) stream_tmp = NULL;
108
109
631
  if (!fu_input_stream_size(stream, &streamsz, error))
110
0
    return NULL;
111
631
  st = fu_struct_bcm57xx_nvram_header_parse_stream(stream, BCM_NVRAM_HEADER_BASE, error);
112
631
  if (st == NULL)
113
0
    return NULL;
114
631
  stage1_wrds = fu_struct_bcm57xx_nvram_header_get_size_wrds(st);
115
631
  stage1_off = fu_struct_bcm57xx_nvram_header_get_offset(st);
116
117
631
  if (stage1_wrds > G_MAXUINT32 / sizeof(guint32)) {
118
10
    g_set_error(error,
119
10
          FWUPD_ERROR,
120
10
          FWUPD_ERROR_NOT_SUPPORTED,
121
10
          "stage1 word count too large: 0x%x",
122
10
          stage1_wrds);
123
10
    return NULL;
124
10
  }
125
621
  stage1_sz = (stage1_wrds * sizeof(guint32));
126
621
  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
592
  if ((gsize)stage1_off + stage1_sz > streamsz) {
136
39
    g_set_error(error,
137
39
          FWUPD_ERROR,
138
39
          FWUPD_ERROR_NOT_SUPPORTED,
139
39
          "bigger than firmware, got: 0x%x @ 0x%x",
140
39
          (guint)stage1_sz,
141
39
          (guint)stage1_off);
142
39
    return NULL;
143
39
  }
144
145
  /* verify CRC */
146
553
  stream_tmp = fu_partial_input_stream_new(stream, stage1_off, stage1_sz, error);
147
553
  if (stream_tmp == NULL)
148
0
    return NULL;
149
553
  if (!fu_firmware_parse_stream(img,
150
553
              stream_tmp,
151
553
              0x0,
152
553
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
153
553
              error))
154
102
    return NULL;
155
156
  /* needed for stage2 */
157
451
  if (out_stage1_sz != NULL)
158
451
    *out_stage1_sz = stage1_sz;
159
160
  /* success */
161
451
  fu_firmware_set_id(img, "stage1");
162
451
  fu_firmware_set_offset(img, stage1_off);
163
451
  return g_steal_pointer(&img);
164
553
}
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
451
{
173
451
  gsize streamsz = 0;
174
451
  guint32 stage2_off = 0;
175
451
  guint32 stage2_sz = 0;
176
451
  g_autoptr(FuFirmware) img = fu_bcm57xx_stage2_image_new();
177
451
  g_autoptr(GInputStream) stream_tmp = NULL;
178
179
451
  stage2_off = BCM_NVRAM_STAGE1_BASE + stage1_sz;
180
451
  if (!fu_bcm57xx_verify_magic(stream, stage2_off, error))
181
135
    return NULL;
182
316
  if (!fu_input_stream_read_u32(stream,
183
316
              stage2_off + sizeof(guint32),
184
316
              &stage2_sz,
185
316
              G_BIG_ENDIAN,
186
316
              error))
187
1
    return NULL;
188
315
  if (!fu_input_stream_size(stream, &streamsz, error))
189
0
    return NULL;
190
315
  if ((gsize)stage2_off + stage2_sz > streamsz) {
191
51
    g_set_error(error,
192
51
          FWUPD_ERROR,
193
51
          FWUPD_ERROR_NOT_SUPPORTED,
194
51
          "bigger than firmware, got: 0x%x @ 0x%x",
195
51
          (guint)stage2_sz,
196
51
          (guint)stage2_off);
197
51
    return NULL;
198
51
  }
199
200
  /* verify CRC */
201
264
  stream_tmp = fu_partial_input_stream_new(stream, stage2_off + 0x8, stage2_sz, error);
202
264
  if (stream_tmp == NULL)
203
8
    return NULL;
204
256
  if (!fu_firmware_parse_stream(img,
205
256
              stream_tmp,
206
256
              0x0,
207
256
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
208
256
              error))
209
3
    return NULL;
210
211
  /* success */
212
253
  fu_firmware_set_id(img, "stage2");
213
253
  fu_firmware_set_offset(img, stage2_off);
214
253
  return g_steal_pointer(&img);
215
256
}
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.72k
{
224
1.72k
  gsize streamsz = 0;
225
1.72k
  guint32 dict_addr = 0x0;
226
1.72k
  guint32 dict_info = 0x0;
227
1.72k
  guint32 dict_off = 0x0;
228
1.72k
  guint32 dict_sz;
229
1.72k
  guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * FU_STRUCT_BCM57XX_NVRAM_DIRECTORY_SIZE);
230
1.72k
  g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new();
231
1.72k
  g_autoptr(FuStructBcm57xxNvramDirectory) st = NULL;
232
1.72k
  g_autoptr(GInputStream) stream_tmp = NULL;
233
234
  /* header */
235
1.72k
  st = fu_struct_bcm57xx_nvram_directory_parse_stream(stream, base, error);
236
1.72k
  if (st == NULL)
237
0
    return FALSE;
238
1.72k
  dict_addr = fu_struct_bcm57xx_nvram_directory_get_addr(st);
239
1.72k
  dict_info = fu_struct_bcm57xx_nvram_directory_get_size_wrds(st);
240
1.72k
  dict_off = fu_struct_bcm57xx_nvram_directory_get_offset(st);
241
242
  /* no dict stored */
243
1.72k
  if (dict_addr == 0 && dict_info == 0 && dict_off == 0)
244
151
    return TRUE;
245
246
1.57k
  dict_sz =
247
1.57k
      (dict_info & 0x00FFFFFF) * sizeof(guint32); /* implies that maximum size is 16 MB */
248
1.57k
  fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img),
249
1.57k
           (dict_info & 0x0F000000) >> 24);
250
1.57k
  fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), (dict_info & 0xF0000000) >> 28);
251
1.57k
  fu_firmware_set_addr(img, dict_addr);
252
1.57k
  fu_firmware_set_offset(img, dict_off);
253
1.57k
  fu_firmware_set_idx(img, 0x80 + idx);
254
255
  /* empty */
256
1.57k
  if (dict_sz == 0) {
257
720
    g_autoptr(GBytes) blob = g_bytes_new(NULL, 0);
258
720
    fu_firmware_set_bytes(img, blob);
259
720
    return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
260
720
  }
261
262
  /* check against image size */
263
850
  if (!fu_input_stream_size(stream, &streamsz, error))
264
0
    return FALSE;
265
850
  if ((gsize)dict_off + (gsize)dict_sz > streamsz) {
266
103
    g_set_error(error,
267
103
          FWUPD_ERROR,
268
103
          FWUPD_ERROR_NOT_SUPPORTED,
269
103
          "bigger than firmware, got: 0x%x @ 0x%x",
270
103
          (guint)dict_sz,
271
103
          (guint)dict_off);
272
103
    return FALSE;
273
103
  }
274
747
  stream_tmp = fu_partial_input_stream_new(stream, dict_off, dict_sz, error);
275
747
  if (stream_tmp == NULL)
276
0
    return FALSE;
277
747
  if (!fu_firmware_parse_stream(img,
278
747
              stream_tmp,
279
747
              0x0,
280
747
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
281
747
              error))
282
0
    return FALSE;
283
284
  /* success */
285
747
  return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
286
747
}
287
288
static gboolean
289
fu_bcm57xx_firmware_validate(FuFirmware *firmware,
290
           GInputStream *stream,
291
           gsize offset,
292
           GError **error)
293
989
{
294
989
  guint32 magic = 0;
295
296
989
  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
983
  if (magic != BCM_APE_HEADER_MAGIC && magic != BCM_STAGE1_HEADER_MAGIC_BROADCOM &&
301
932
      magic != BCM_STAGE1_HEADER_MAGIC_MEKLORT && magic != BCM_NVRAM_MAGIC) {
302
147
    g_set_error(error,
303
147
          FWUPD_ERROR,
304
147
          FWUPD_ERROR_INVALID_FILE,
305
147
          "file not supported, got: 0x%08X",
306
147
          magic);
307
147
    return FALSE;
308
147
  }
309
310
  /* success */
311
836
  return TRUE;
312
983
}
313
314
static gboolean
315
fu_bcm57xx_firmware_parse(FuFirmware *firmware,
316
        GInputStream *stream,
317
        FuFirmwareParseFlags flags,
318
        GError **error)
319
836
{
320
836
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
321
836
  gsize streamsz = 0;
322
836
  guint32 magic = 0;
323
836
  guint32 stage1_sz = 0;
324
836
  g_autoptr(FuFirmware) img_info2 = fu_firmware_new();
325
836
  g_autoptr(FuFirmware) img_info = NULL;
326
836
  g_autoptr(FuFirmware) img_stage1 = NULL;
327
836
  g_autoptr(FuFirmware) img_stage2 = NULL;
328
836
  g_autoptr(FuFirmware) img_vpd = fu_firmware_new();
329
836
  g_autoptr(GInputStream) stream_header = NULL;
330
836
  g_autoptr(GInputStream) stream_info2 = NULL;
331
836
  g_autoptr(GInputStream) stream_info = NULL;
332
836
  g_autoptr(GInputStream) stream_vpd = NULL;
333
334
  /* try to autodetect the file type */
335
836
  if (!fu_input_stream_read_u32(stream, 0x0, &magic, G_BIG_ENDIAN, error))
336
0
    return FALSE;
337
338
  /* standalone APE */
339
836
  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
834
  if (magic == BCM_STAGE1_HEADER_MAGIC_BROADCOM || magic == BCM_STAGE1_HEADER_MAGIC_MEKLORT) {
350
84
    g_autoptr(FuFirmware) img_stage1_standalone = fu_firmware_new();
351
84
    if (!fu_firmware_set_stream(img_stage1_standalone, stream, error))
352
0
      return FALSE;
353
84
    fu_firmware_set_id(img_stage1_standalone, "stage1");
354
84
    return fu_firmware_add_image(firmware, img_stage1_standalone, error);
355
84
  }
356
357
  /* not full NVRAM image */
358
750
  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
750
  if (!fu_input_stream_size(stream, &streamsz, error))
369
0
    return FALSE;
370
750
  self->source_size = streamsz;
371
750
  if (!fu_input_stream_read_u8(stream, streamsz - 1, &self->source_padchar, error))
372
0
    return FALSE;
373
374
  /* NVRAM header */
375
750
  stream_header = fu_partial_input_stream_new(stream,
376
750
                BCM_NVRAM_HEADER_BASE,
377
750
                FU_STRUCT_BCM57XX_NVRAM_HEADER_SIZE,
378
750
                error);
379
750
  if (stream_header == NULL)
380
9
    return FALSE;
381
741
  if (!fu_bcm57xx_firmware_parse_header(self, stream_header, error)) {
382
50
    g_prefix_error_literal(error, "failed to parse header: ");
383
50
    return FALSE;
384
50
  }
385
386
  /* info */
387
691
  stream_info = fu_partial_input_stream_new(stream,
388
691
              BCM_NVRAM_INFO_BASE,
389
691
              FU_STRUCT_BCM57XX_NVRAM_INFO_SIZE,
390
691
              error);
391
691
  if (stream_info == NULL)
392
7
    return FALSE;
393
684
  img_info = fu_bcm57xx_firmware_parse_info(self, stream_info, flags, error);
394
684
  if (img_info == NULL) {
395
0
    g_prefix_error_literal(error, "failed to parse info: ");
396
0
    return FALSE;
397
0
  }
398
684
  fu_firmware_set_offset(img_info, BCM_NVRAM_INFO_BASE);
399
684
  if (!fu_firmware_add_image(firmware, img_info, error))
400
0
    return FALSE;
401
402
  /* VPD */
403
684
  stream_vpd =
404
684
      fu_partial_input_stream_new(stream, BCM_NVRAM_VPD_BASE, BCM_NVRAM_VPD_SZ, error);
405
684
  if (stream_vpd == NULL)
406
48
    return FALSE;
407
636
  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
636
  fu_firmware_set_id(img_vpd, "vpd");
412
636
  fu_firmware_set_offset(img_vpd, BCM_NVRAM_VPD_BASE);
413
636
  if (!fu_firmware_add_image(firmware, img_vpd, error))
414
0
    return FALSE;
415
416
  /* info2 */
417
636
  stream_info2 =
418
636
      fu_partial_input_stream_new(stream, BCM_NVRAM_INFO2_BASE, BCM_NVRAM_INFO2_SZ, error);
419
636
  if (stream_info2 == NULL)
420
5
    return FALSE;
421
631
  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
631
  fu_firmware_set_id(img_info2, "info2");
426
631
  fu_firmware_set_offset(img_info2, BCM_NVRAM_INFO2_BASE);
427
631
  if (!fu_firmware_add_image(firmware, img_info2, error))
428
0
    return FALSE;
429
430
  /* stage1 */
431
631
  img_stage1 = fu_bcm57xx_firmware_parse_stage1(self, stream, &stage1_sz, flags, error);
432
631
  if (img_stage1 == NULL) {
433
180
    g_prefix_error_literal(error, "failed to parse stage1: ");
434
180
    return FALSE;
435
180
  }
436
451
  if (!fu_firmware_add_image(firmware, img_stage1, error))
437
0
    return FALSE;
438
439
  /* stage2 */
440
451
  img_stage2 = fu_bcm57xx_firmware_parse_stage2(self, stream, stage1_sz, flags, error);
441
451
  if (img_stage2 == NULL) {
442
198
    g_prefix_error_literal(error, "failed to parse stage2: ");
443
198
    return FALSE;
444
198
  }
445
253
  if (!fu_firmware_add_image(firmware, img_stage2, error))
446
0
    return FALSE;
447
448
  /* dictionaries, e.g. APE */
449
1.87k
  for (guint i = 0; i < 8; i++) {
450
1.72k
    if (!fu_bcm57xx_firmware_parse_dict(self, stream, i, flags, error)) {
451
103
      g_prefix_error(error, "failed to parse dict 0x%x: ", i);
452
103
      return FALSE;
453
103
    }
454
1.72k
  }
455
456
  /* success */
457
150
  return TRUE;
458
253
}
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
236
{
490
236
  gsize off = BCM_NVRAM_STAGE1_BASE;
491
236
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
492
236
  g_autoptr(GByteArray) buf = g_byte_array_sized_new(self->source_size);
493
236
  g_autoptr(FuFirmware) img_info2 = NULL;
494
236
  g_autoptr(FuFirmware) img_info = NULL;
495
236
  g_autoptr(FuFirmware) img_stage1 = NULL;
496
236
  g_autoptr(FuFirmware) img_stage2 = NULL;
497
236
  g_autoptr(FuFirmware) img_vpd = NULL;
498
236
  g_autoptr(GBytes) blob_info2 = NULL;
499
236
  g_autoptr(GBytes) blob_info = NULL;
500
236
  g_autoptr(GBytes) blob_stage1 = NULL;
501
236
  g_autoptr(GBytes) blob_stage2 = NULL;
502
236
  g_autoptr(GBytes) blob_vpd = NULL;
503
236
  g_autoptr(GPtrArray) blob_dicts = NULL;
504
505
  /* write out the things we need to pre-compute */
506
236
  img_stage1 = fu_firmware_get_image_by_id(firmware, "stage1", error);
507
236
  if (img_stage1 == NULL)
508
2
    return NULL;
509
234
  blob_stage1 = fu_firmware_write(img_stage1, error);
510
234
  if (blob_stage1 == NULL)
511
26
    return NULL;
512
208
  off += g_bytes_get_size(blob_stage1);
513
208
  img_stage2 = fu_firmware_get_image_by_id(firmware, "stage2", error);
514
208
  if (img_stage2 == NULL)
515
84
    return NULL;
516
124
  blob_stage2 = fu_firmware_write(img_stage2, error);
517
124
  if (blob_stage2 == NULL)
518
5
    return NULL;
519
119
  off += g_bytes_get_size(blob_stage2);
520
521
  /* add header */
522
119
  fu_byte_array_append_uint32(buf, BCM_NVRAM_MAGIC, G_BIG_ENDIAN);
523
119
  fu_byte_array_append_uint32(buf, self->phys_addr, G_BIG_ENDIAN);
524
119
  fu_byte_array_append_uint32(buf,
525
119
            g_bytes_get_size(blob_stage1) / sizeof(guint32),
526
119
            G_BIG_ENDIAN);
527
119
  fu_byte_array_append_uint32(buf, BCM_NVRAM_STAGE1_BASE, G_BIG_ENDIAN);
528
119
  fu_byte_array_append_uint32(buf,
529
119
            fu_crc32(FU_CRC_KIND_B32_STANDARD, buf->data, buf->len),
530
119
            G_LITTLE_ENDIAN);
531
532
  /* add directory entries */
533
119
  blob_dicts = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref);
534
971
  for (guint i = 0; i < 8; i++) {
535
874
    g_autoptr(FuFirmware) img = NULL;
536
874
    g_autoptr(GBytes) blob = NULL;
537
538
874
    img = fu_firmware_get_image_by_idx(firmware, 0x80 + i, NULL);
539
874
    if (img != NULL) {
540
601
      blob = fu_firmware_write(img, error);
541
601
      if (blob == NULL)
542
22
        return NULL;
543
601
    }
544
852
    if (blob != NULL) {
545
579
      fu_byte_array_append_uint32(buf, fu_firmware_get_addr(img), G_BIG_ENDIAN);
546
579
      fu_byte_array_append_uint32(
547
579
          buf,
548
579
          (g_bytes_get_size(blob) / sizeof(guint32)) |
549
579
        (guint32)fu_bcm57xx_dict_image_get_target(
550
579
            FU_BCM57XX_DICT_IMAGE(img))
551
579
            << 24 |
552
579
        (guint32)fu_bcm57xx_dict_image_get_kind(FU_BCM57XX_DICT_IMAGE(img))
553
579
            << 28,
554
579
          G_BIG_ENDIAN);
555
579
      if (g_bytes_get_size(blob) > 0) {
556
579
        fu_byte_array_append_uint32(buf, off, G_BIG_ENDIAN);
557
579
        off += g_bytes_get_size(blob);
558
579
      } else {
559
0
        fu_byte_array_append_uint32(buf, 0x0, G_BIG_ENDIAN);
560
0
      }
561
579
    } else {
562
273
      blob = g_bytes_new(NULL, 0);
563
3.54k
      for (guint32 j = 0; j < sizeof(guint32) * 3; j++)
564
3.27k
        fu_byte_array_append_uint8(buf, 0x0);
565
273
    }
566
852
    g_ptr_array_add(blob_dicts, g_steal_pointer(&blob));
567
852
  }
568
569
  /* add info */
570
97
  img_info = fu_firmware_get_image_by_id(firmware, "info", NULL);
571
97
  if (img_info != NULL) {
572
97
    blob_info = fu_firmware_write(img_info, error);
573
97
    if (blob_info == NULL)
574
97
      return NULL;
575
97
  } 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
989
{
638
989
  self->phys_addr = BCM_PHYS_ADDR_DEFAULT;
639
989
  self->source_size = BCM_FIRMWARE_SIZE;
640
989
  self->source_padchar = 0xff;
641
989
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID);
642
989
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
643
989
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
644
989
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
645
989
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_BCM57XX_STAGE1_IMAGE);
646
989
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_BCM57XX_STAGE2_IMAGE);
647
989
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_BCM57XX_DICT_IMAGE);
648
989
  fu_firmware_set_size_max(FU_FIRMWARE(self), 16 * FU_MB);
649
989
}
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
}