Coverage Report

Created: 2025-11-24 06:59

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
4.26k
G_DEFINE_TYPE(FuBcm57xxFirmware, fu_bcm57xx_firmware, FU_TYPE_FIRMWARE)
28
4.26k
29
22.7k
#define BCM_STAGE1_HEADER_MAGIC_BROADCOM 0x0E000E03
30
20.7k
#define BCM_STAGE1_HEADER_MAGIC_MEKLORT  0x3C1D0800
31
32
20.9k
#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
1.68k
{
51
  /* verify magic and CRC */
52
1.68k
  if (!fu_bcm57xx_verify_magic(stream, 0x0, error))
53
0
    return FALSE;
54
1.68k
  if (!fu_bcm57xx_verify_crc(stream, error))
55
122
    return FALSE;
56
57
  /* get address */
58
1.56k
  return fu_input_stream_read_u32(stream,
59
1.56k
          FU_STRUCT_BCM57XX_NVRAM_HEADER_OFFSET_PHYS_ADDR,
60
1.56k
          &self->phys_addr,
61
1.56k
          G_BIG_ENDIAN,
62
1.56k
          error);
63
1.68k
}
64
65
static FuFirmware *
66
fu_bcm57xx_firmware_parse_info(FuBcm57xxFirmware *self,
67
             GInputStream *stream,
68
             FuFirmwareParseFlags flags,
69
             GError **error)
70
1.54k
{
71
1.54k
  guint32 mac_addr0;
72
1.54k
  g_autoptr(FuFirmware) img = fu_firmware_new();
73
1.54k
  g_autoptr(FuStructBcm57xxNvramInfo) st = NULL;
74
75
1.54k
  st = fu_struct_bcm57xx_nvram_info_parse_stream(stream, 0x0, error);
76
1.54k
  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
1.54k
  mac_addr0 = fu_struct_bcm57xx_nvram_info_get_mac_addr(st, 0);
81
1.54k
  self->is_backup = mac_addr0 != 0x0 && mac_addr0 != 0xffffffff;
82
83
  /* read vendor + model */
84
1.54k
  self->vendor = fu_struct_bcm57xx_nvram_info_get_vendor(st);
85
1.54k
  self->model = fu_struct_bcm57xx_nvram_info_get_device(st);
86
87
  /* success */
88
1.54k
  if (!fu_firmware_parse_stream(img, stream, 0x0, flags, error))
89
0
    return NULL;
90
1.54k
  fu_firmware_set_id(img, "info");
91
1.54k
  return g_steal_pointer(&img);
92
1.54k
}
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
1.44k
{
101
1.44k
  gsize streamsz = 0;
102
1.44k
  guint32 stage1_wrds = 0;
103
1.44k
  guint32 stage1_sz;
104
1.44k
  guint32 stage1_off = 0;
105
1.44k
  g_autoptr(FuStructBcm57xxNvramHeader) st = NULL;
106
1.44k
  g_autoptr(FuFirmware) img = fu_bcm57xx_stage1_image_new();
107
1.44k
  g_autoptr(GInputStream) stream_tmp = NULL;
108
109
1.44k
  if (!fu_input_stream_size(stream, &streamsz, error))
110
0
    return NULL;
111
1.44k
  st = fu_struct_bcm57xx_nvram_header_parse_stream(stream, BCM_NVRAM_HEADER_BASE, error);
112
1.44k
  if (st == NULL)
113
0
    return NULL;
114
1.44k
  stage1_wrds = fu_struct_bcm57xx_nvram_header_get_size_wrds(st);
115
1.44k
  stage1_off = fu_struct_bcm57xx_nvram_header_get_offset(st);
116
117
1.44k
  stage1_sz = (stage1_wrds * sizeof(guint32));
118
1.44k
  if (stage1_off != BCM_NVRAM_STAGE1_BASE) {
119
48
    g_set_error(error,
120
48
          FWUPD_ERROR,
121
48
          FWUPD_ERROR_NOT_SUPPORTED,
122
48
          "stage1 offset invalid, got: 0x%x, expected 0x%x",
123
48
          (guint)stage1_sz,
124
48
          (guint)BCM_NVRAM_STAGE1_BASE);
125
48
    return NULL;
126
48
  }
127
1.39k
  if (stage1_off + stage1_sz > streamsz) {
128
26
    g_set_error(error,
129
26
          FWUPD_ERROR,
130
26
          FWUPD_ERROR_NOT_SUPPORTED,
131
26
          "bigger than firmware, got: 0x%x @ 0x%x",
132
26
          (guint)stage1_sz,
133
26
          (guint)stage1_off);
134
26
    return NULL;
135
26
  }
136
137
  /* verify CRC */
138
1.36k
  stream_tmp = fu_partial_input_stream_new(stream, stage1_off, stage1_sz, error);
139
1.36k
  if (stream_tmp == NULL)
140
32
    return NULL;
141
1.33k
  if (!fu_firmware_parse_stream(img,
142
1.33k
              stream_tmp,
143
1.33k
              0x0,
144
1.33k
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
145
1.33k
              error))
146
519
    return NULL;
147
148
  /* needed for stage2 */
149
815
  if (out_stage1_sz != NULL)
150
815
    *out_stage1_sz = stage1_sz;
151
152
  /* success */
153
815
  fu_firmware_set_id(img, "stage1");
154
815
  fu_firmware_set_offset(img, stage1_off);
155
815
  return g_steal_pointer(&img);
156
1.33k
}
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
815
{
165
815
  gsize streamsz = 0;
166
815
  guint32 stage2_off = 0;
167
815
  guint32 stage2_sz = 0;
168
815
  g_autoptr(FuFirmware) img = fu_bcm57xx_stage2_image_new();
169
815
  g_autoptr(GInputStream) stream_tmp = NULL;
170
171
815
  stage2_off = BCM_NVRAM_STAGE1_BASE + stage1_sz;
172
815
  if (!fu_bcm57xx_verify_magic(stream, stage2_off, error))
173
172
    return NULL;
174
643
  if (!fu_input_stream_read_u32(stream,
175
643
              stage2_off + sizeof(guint32),
176
643
              &stage2_sz,
177
643
              G_BIG_ENDIAN,
178
643
              error))
179
3
    return NULL;
180
640
  if (!fu_input_stream_size(stream, &streamsz, error))
181
0
    return NULL;
182
640
  if (stage2_off + stage2_sz > streamsz) {
183
34
    g_set_error(error,
184
34
          FWUPD_ERROR,
185
34
          FWUPD_ERROR_NOT_SUPPORTED,
186
34
          "bigger than firmware, got: 0x%x @ 0x%x",
187
34
          (guint)stage2_sz,
188
34
          (guint)stage2_off);
189
34
    return NULL;
190
34
  }
191
192
  /* verify CRC */
193
606
  stream_tmp = fu_partial_input_stream_new(stream, stage2_off + 0x8, stage2_sz, error);
194
606
  if (stream_tmp == NULL)
195
24
    return NULL;
196
582
  if (!fu_firmware_parse_stream(img,
197
582
              stream_tmp,
198
582
              0x0,
199
582
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
200
582
              error))
201
69
    return NULL;
202
203
  /* success */
204
513
  fu_firmware_set_id(img, "stage2");
205
513
  fu_firmware_set_offset(img, stage2_off);
206
513
  return g_steal_pointer(&img);
207
582
}
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
2.95k
{
216
2.95k
  gsize streamsz = 0;
217
2.95k
  guint32 dict_addr = 0x0;
218
2.95k
  guint32 dict_info = 0x0;
219
2.95k
  guint32 dict_off = 0x0;
220
2.95k
  guint32 dict_sz;
221
2.95k
  guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * FU_STRUCT_BCM57XX_NVRAM_DIRECTORY_SIZE);
222
2.95k
  g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new();
223
2.95k
  g_autoptr(FuStructBcm57xxNvramDirectory) st = NULL;
224
2.95k
  g_autoptr(GInputStream) stream_tmp = NULL;
225
226
  /* header */
227
2.95k
  st = fu_struct_bcm57xx_nvram_directory_parse_stream(stream, base, error);
228
2.95k
  if (st == NULL)
229
0
    return FALSE;
230
2.95k
  dict_addr = fu_struct_bcm57xx_nvram_directory_get_addr(st);
231
2.95k
  dict_info = fu_struct_bcm57xx_nvram_directory_get_size_wrds(st);
232
2.95k
  dict_off = fu_struct_bcm57xx_nvram_directory_get_offset(st);
233
234
  /* no dict stored */
235
2.95k
  if (dict_addr == 0 && dict_info == 0 && dict_off == 0)
236
194
    return TRUE;
237
238
2.76k
  dict_sz =
239
2.76k
      (dict_info & 0x00FFFFFF) * sizeof(guint32); /* implies that maximum size is 16 MB */
240
2.76k
  fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img),
241
2.76k
           (dict_info & 0x0F000000) >> 24);
242
2.76k
  fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), (dict_info & 0xF0000000) >> 28);
243
2.76k
  fu_firmware_set_addr(img, dict_addr);
244
2.76k
  fu_firmware_set_offset(img, dict_off);
245
2.76k
  fu_firmware_set_idx(img, 0x80 + idx);
246
247
  /* empty */
248
2.76k
  if (dict_sz == 0) {
249
1.08k
    g_autoptr(GBytes) blob = g_bytes_new(NULL, 0);
250
1.08k
    fu_firmware_set_bytes(img, blob);
251
1.08k
    return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
252
1.08k
  }
253
254
  /* check against image size */
255
1.67k
  if (!fu_input_stream_size(stream, &streamsz, error))
256
0
    return FALSE;
257
1.67k
  if (dict_off + dict_sz > streamsz) {
258
178
    g_set_error(error,
259
178
          FWUPD_ERROR,
260
178
          FWUPD_ERROR_NOT_SUPPORTED,
261
178
          "bigger than firmware, got: 0x%x @ 0x%x",
262
178
          (guint)dict_sz,
263
178
          (guint)dict_off);
264
178
    return FALSE;
265
178
  }
266
1.50k
  stream_tmp = fu_partial_input_stream_new(stream, dict_off, dict_sz, error);
267
1.50k
  if (stream_tmp == NULL)
268
37
    return FALSE;
269
1.46k
  if (!fu_firmware_parse_stream(img,
270
1.46k
              stream_tmp,
271
1.46k
              0x0,
272
1.46k
              flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
273
1.46k
              error))
274
122
    return FALSE;
275
276
  /* success */
277
1.34k
  return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
278
1.46k
}
279
280
static gboolean
281
fu_bcm57xx_firmware_validate(FuFirmware *firmware,
282
           GInputStream *stream,
283
           gsize offset,
284
           GError **error)
285
9.59k
{
286
9.59k
  guint32 magic = 0;
287
288
9.59k
  if (!fu_input_stream_read_u32(stream, 0x0, &magic, G_BIG_ENDIAN, error)) {
289
30
    g_prefix_error_literal(error, "failed to read magic: ");
290
30
    return FALSE;
291
30
  }
292
9.56k
  if (magic != BCM_APE_HEADER_MAGIC && magic != BCM_STAGE1_HEADER_MAGIC_BROADCOM &&
293
9.50k
      magic != BCM_STAGE1_HEADER_MAGIC_MEKLORT && magic != BCM_NVRAM_MAGIC) {
294
7.78k
    g_set_error(error,
295
7.78k
          FWUPD_ERROR,
296
7.78k
          FWUPD_ERROR_INVALID_FILE,
297
7.78k
          "file not supported, got: 0x%08X",
298
7.78k
          magic);
299
7.78k
    return FALSE;
300
7.78k
  }
301
302
  /* success */
303
1.78k
  return TRUE;
304
9.56k
}
305
306
static gboolean
307
fu_bcm57xx_firmware_parse(FuFirmware *firmware,
308
        GInputStream *stream,
309
        FuFirmwareParseFlags flags,
310
        GError **error)
311
1.78k
{
312
1.78k
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
313
1.78k
  gsize streamsz = 0;
314
1.78k
  guint32 magic = 0;
315
1.78k
  guint32 stage1_sz = 0;
316
1.78k
  g_autoptr(FuFirmware) img_info2 = fu_firmware_new();
317
1.78k
  g_autoptr(FuFirmware) img_info = NULL;
318
1.78k
  g_autoptr(FuFirmware) img_stage1 = NULL;
319
1.78k
  g_autoptr(FuFirmware) img_stage2 = NULL;
320
1.78k
  g_autoptr(FuFirmware) img_vpd = fu_firmware_new();
321
1.78k
  g_autoptr(GInputStream) stream_header = NULL;
322
1.78k
  g_autoptr(GInputStream) stream_info2 = NULL;
323
1.78k
  g_autoptr(GInputStream) stream_info = NULL;
324
1.78k
  g_autoptr(GInputStream) stream_vpd = NULL;
325
326
  /* try to autodetect the file type */
327
1.78k
  if (!fu_input_stream_read_u32(stream, 0x0, &magic, G_BIG_ENDIAN, error))
328
0
    return FALSE;
329
330
  /* standalone APE */
331
1.78k
  if (magic == BCM_APE_HEADER_MAGIC) {
332
2
    g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new();
333
2
    fu_bcm57xx_dict_image_set_target(FU_BCM57XX_DICT_IMAGE(img), 0xD);
334
2
    fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), 0x0);
335
2
    fu_firmware_set_addr(img, BCM_CODE_DIRECTORY_ADDR_APE);
336
2
    fu_firmware_set_id(img, "ape");
337
2
    return fu_firmware_add_image(firmware, img, error);
338
2
  }
339
340
  /* standalone stage1 */
341
1.78k
  if (magic == BCM_STAGE1_HEADER_MAGIC_BROADCOM || magic == BCM_STAGE1_HEADER_MAGIC_MEKLORT) {
342
80
    g_autoptr(FuFirmware) img_stage1_standalone = fu_firmware_new();
343
80
    if (!fu_firmware_set_stream(img_stage1_standalone, stream, error))
344
0
      return FALSE;
345
80
    fu_firmware_set_id(img_stage1_standalone, "stage1");
346
80
    return fu_firmware_add_image(firmware, img_stage1_standalone, error);
347
80
  }
348
349
  /* not full NVRAM image */
350
1.70k
  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
1.70k
  if (!fu_input_stream_size(stream, &streamsz, error))
361
0
    return FALSE;
362
1.70k
  self->source_size = streamsz;
363
1.70k
  if (!fu_input_stream_read_u8(stream, streamsz - 1, &self->source_padchar, error))
364
0
    return FALSE;
365
366
  /* NVRAM header */
367
1.70k
  stream_header = fu_partial_input_stream_new(stream,
368
1.70k
                BCM_NVRAM_HEADER_BASE,
369
1.70k
                FU_STRUCT_BCM57XX_NVRAM_HEADER_SIZE,
370
1.70k
                error);
371
1.70k
  if (stream_header == NULL)
372
22
    return FALSE;
373
1.68k
  if (!fu_bcm57xx_firmware_parse_header(self, stream_header, error)) {
374
122
    g_prefix_error_literal(error, "failed to parse header: ");
375
122
    return FALSE;
376
122
  }
377
378
  /* info */
379
1.56k
  stream_info = fu_partial_input_stream_new(stream,
380
1.56k
              BCM_NVRAM_INFO_BASE,
381
1.56k
              FU_STRUCT_BCM57XX_NVRAM_INFO_SIZE,
382
1.56k
              error);
383
1.56k
  if (stream_info == NULL)
384
16
    return FALSE;
385
1.54k
  img_info = fu_bcm57xx_firmware_parse_info(self, stream_info, flags, error);
386
1.54k
  if (img_info == NULL) {
387
0
    g_prefix_error_literal(error, "failed to parse info: ");
388
0
    return FALSE;
389
0
  }
390
1.54k
  fu_firmware_set_offset(img_info, BCM_NVRAM_INFO_BASE);
391
1.54k
  if (!fu_firmware_add_image(firmware, img_info, error))
392
0
    return FALSE;
393
394
  /* VPD */
395
1.54k
  stream_vpd =
396
1.54k
      fu_partial_input_stream_new(stream, BCM_NVRAM_VPD_BASE, BCM_NVRAM_VPD_SZ, error);
397
1.54k
  if (stream_vpd == NULL)
398
90
    return FALSE;
399
1.45k
  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
1.45k
  fu_firmware_set_id(img_vpd, "vpd");
404
1.45k
  fu_firmware_set_offset(img_vpd, BCM_NVRAM_VPD_BASE);
405
1.45k
  if (!fu_firmware_add_image(firmware, img_vpd, error))
406
0
    return FALSE;
407
408
  /* info2 */
409
1.45k
  stream_info2 =
410
1.45k
      fu_partial_input_stream_new(stream, BCM_NVRAM_INFO2_BASE, BCM_NVRAM_INFO2_SZ, error);
411
1.45k
  if (stream_info2 == NULL)
412
14
    return FALSE;
413
1.44k
  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
1.44k
  fu_firmware_set_id(img_info2, "info2");
418
1.44k
  fu_firmware_set_offset(img_info2, BCM_NVRAM_INFO2_BASE);
419
1.44k
  if (!fu_firmware_add_image(firmware, img_info2, error))
420
0
    return FALSE;
421
422
  /* stage1 */
423
1.44k
  img_stage1 = fu_bcm57xx_firmware_parse_stage1(self, stream, &stage1_sz, flags, error);
424
1.44k
  if (img_stage1 == NULL) {
425
625
    g_prefix_error_literal(error, "failed to parse stage1: ");
426
625
    return FALSE;
427
625
  }
428
815
  if (!fu_firmware_add_image(firmware, img_stage1, error))
429
0
    return FALSE;
430
431
  /* stage2 */
432
815
  img_stage2 = fu_bcm57xx_firmware_parse_stage2(self, stream, stage1_sz, flags, error);
433
815
  if (img_stage2 == NULL) {
434
302
    g_prefix_error_literal(error, "failed to parse stage2: ");
435
302
    return FALSE;
436
302
  }
437
513
  if (!fu_firmware_add_image(firmware, img_stage2, error))
438
0
    return FALSE;
439
440
  /* dictionaries, e.g. APE */
441
3.13k
  for (guint i = 0; i < 8; i++) {
442
2.95k
    if (!fu_bcm57xx_firmware_parse_dict(self, stream, i, flags, error)) {
443
337
      g_prefix_error(error, "failed to parse dict 0x%x: ", i);
444
337
      return FALSE;
445
337
    }
446
2.95k
  }
447
448
  /* success */
449
176
  return TRUE;
450
513
}
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
258
{
482
258
  gsize off = BCM_NVRAM_STAGE1_BASE;
483
258
  FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware);
484
258
  g_autoptr(GByteArray) buf = g_byte_array_sized_new(self->source_size);
485
258
  g_autoptr(FuFirmware) img_info2 = NULL;
486
258
  g_autoptr(FuFirmware) img_info = NULL;
487
258
  g_autoptr(FuFirmware) img_stage1 = NULL;
488
258
  g_autoptr(FuFirmware) img_stage2 = NULL;
489
258
  g_autoptr(FuFirmware) img_vpd = NULL;
490
258
  g_autoptr(GBytes) blob_info2 = NULL;
491
258
  g_autoptr(GBytes) blob_info = NULL;
492
258
  g_autoptr(GBytes) blob_stage1 = NULL;
493
258
  g_autoptr(GBytes) blob_stage2 = NULL;
494
258
  g_autoptr(GBytes) blob_vpd = NULL;
495
258
  g_autoptr(GPtrArray) blob_dicts = NULL;
496
497
  /* write out the things we need to pre-compute */
498
258
  img_stage1 = fu_firmware_get_image_by_id(firmware, "stage1", error);
499
258
  if (img_stage1 == NULL)
500
2
    return NULL;
501
256
  blob_stage1 = fu_firmware_write(img_stage1, error);
502
256
  if (blob_stage1 == NULL)
503
51
    return NULL;
504
205
  off += g_bytes_get_size(blob_stage1);
505
205
  img_stage2 = fu_firmware_get_image_by_id(firmware, "stage2", error);
506
205
  if (img_stage2 == NULL)
507
80
    return NULL;
508
125
  blob_stage2 = fu_firmware_write(img_stage2, error);
509
125
  if (blob_stage2 == NULL)
510
13
    return NULL;
511
112
  off += g_bytes_get_size(blob_stage2);
512
513
  /* add header */
514
112
  fu_byte_array_append_uint32(buf, BCM_NVRAM_MAGIC, G_BIG_ENDIAN);
515
112
  fu_byte_array_append_uint32(buf, self->phys_addr, G_BIG_ENDIAN);
516
112
  fu_byte_array_append_uint32(buf,
517
112
            g_bytes_get_size(blob_stage1) / sizeof(guint32),
518
112
            G_BIG_ENDIAN);
519
112
  fu_byte_array_append_uint32(buf, BCM_NVRAM_STAGE1_BASE, G_BIG_ENDIAN);
520
112
  fu_byte_array_append_uint32(buf,
521
112
            fu_crc32(FU_CRC_KIND_B32_STANDARD, buf->data, buf->len),
522
112
            G_LITTLE_ENDIAN);
523
524
  /* add directory entries */
525
112
  blob_dicts = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref);
526
900
  for (guint i = 0; i < 8; i++) {
527
812
    g_autoptr(FuFirmware) img = NULL;
528
812
    g_autoptr(GBytes) blob = NULL;
529
530
812
    img = fu_firmware_get_image_by_idx(firmware, 0x80 + i, NULL);
531
812
    if (img != NULL) {
532
597
      blob = fu_firmware_write(img, error);
533
597
      if (blob == NULL)
534
24
        return NULL;
535
597
    }
536
788
    if (blob != NULL) {
537
573
      fu_byte_array_append_uint32(buf, fu_firmware_get_addr(img), G_BIG_ENDIAN);
538
573
      fu_byte_array_append_uint32(
539
573
          buf,
540
573
          (g_bytes_get_size(blob) / sizeof(guint32)) |
541
573
        (guint32)fu_bcm57xx_dict_image_get_target(
542
573
            FU_BCM57XX_DICT_IMAGE(img))
543
573
            << 24 |
544
573
        (guint32)fu_bcm57xx_dict_image_get_kind(FU_BCM57XX_DICT_IMAGE(img))
545
573
            << 28,
546
573
          G_BIG_ENDIAN);
547
573
      if (g_bytes_get_size(blob) > 0) {
548
573
        fu_byte_array_append_uint32(buf, off, G_BIG_ENDIAN);
549
573
        off += g_bytes_get_size(blob);
550
573
      } else {
551
0
        fu_byte_array_append_uint32(buf, 0x0, G_BIG_ENDIAN);
552
0
      }
553
573
    } else {
554
215
      blob = g_bytes_new(NULL, 0);
555
2.79k
      for (guint32 j = 0; j < sizeof(guint32) * 3; j++)
556
2.58k
        fu_byte_array_append_uint8(buf, 0x0);
557
215
    }
558
788
    g_ptr_array_add(blob_dicts, g_steal_pointer(&blob));
559
788
  }
560
561
  /* add info */
562
88
  img_info = fu_firmware_get_image_by_id(firmware, "info", NULL);
563
88
  if (img_info != NULL) {
564
88
    blob_info = fu_firmware_write(img_info, error);
565
88
    if (blob_info == NULL)
566
88
      return NULL;
567
88
  } 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
gboolean
628
fu_bcm57xx_firmware_is_backup(FuBcm57xxFirmware *self)
629
0
{
630
0
  return self->is_backup;
631
0
}
632
633
static void
634
fu_bcm57xx_firmware_init(FuBcm57xxFirmware *self)
635
2.22k
{
636
2.22k
  self->phys_addr = BCM_PHYS_ADDR_DEFAULT;
637
2.22k
  self->source_size = BCM_FIRMWARE_SIZE;
638
2.22k
  self->source_padchar = 0xff;
639
2.22k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID);
640
2.22k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM);
641
2.22k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
642
2.22k
  g_type_ensure(FU_TYPE_BCM57XX_STAGE1_IMAGE);
643
2.22k
  g_type_ensure(FU_TYPE_BCM57XX_STAGE2_IMAGE);
644
2.22k
  g_type_ensure(FU_TYPE_BCM57XX_DICT_IMAGE);
645
2.22k
}
646
647
static void
648
fu_bcm57xx_firmware_class_init(FuBcm57xxFirmwareClass *klass)
649
1
{
650
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
651
1
  firmware_class->validate = fu_bcm57xx_firmware_validate;
652
1
  firmware_class->parse = fu_bcm57xx_firmware_parse;
653
1
  firmware_class->export = fu_bcm57xx_firmware_export;
654
1
  firmware_class->write = fu_bcm57xx_firmware_write;
655
1
  firmware_class->build = fu_bcm57xx_firmware_build;
656
1
}
657
658
FuFirmware *
659
fu_bcm57xx_firmware_new(void)
660
0
{
661
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_BCM57XX_FIRMWARE, NULL));
662
0
}