Coverage Report

Created: 2025-07-11 06:31

/src/fwupd/libfwupdplugin/fu-fdt-firmware.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
578k
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-bytes.h"
13
#include "fu-common.h"
14
#include "fu-dump.h"
15
#include "fu-fdt-firmware.h"
16
#include "fu-fdt-image.h"
17
#include "fu-fdt-struct.h"
18
#include "fu-input-stream.h"
19
#include "fu-mem.h"
20
21
/**
22
 * FuFdtFirmware:
23
 *
24
 * A Flattened DeviceTree firmware image.
25
 *
26
 * Documented:
27
 * https://devicetree-specification.readthedocs.io/en/latest/chapter5-flattened-format.html
28
 *
29
 * See also: [class@FuFirmware]
30
 */
31
32
typedef struct {
33
  guint32 cpuid;
34
} FuFdtFirmwarePrivate;
35
36
G_DEFINE_TYPE_WITH_PRIVATE(FuFdtFirmware, fu_fdt_firmware, FU_TYPE_FIRMWARE)
37
5.37k
#define GET_PRIVATE(o) (fu_fdt_firmware_get_instance_private(o))
38
39
3.65k
#define FDT_LAST_COMP_VERSION 2
40
130k
#define FDT_DEPTH_MAX       128
41
42
static GString *
43
fu_fdt_firmware_string_new_safe(const guint8 *buf, gsize bufsz, gsize offset, GError **error)
44
171k
{
45
171k
  g_autoptr(GString) str = g_string_new(NULL);
46
1.76G
  for (gsize i = offset; i < bufsz; i++) {
47
1.76G
    if (buf[i] == '\0')
48
170k
      return g_steal_pointer(&str);
49
1.76G
    g_string_append_c(str, (gchar)buf[i]);
50
1.76G
  }
51
127
  g_set_error_literal(error,
52
127
          FWUPD_ERROR,
53
127
          FWUPD_ERROR_INVALID_DATA,
54
127
          "buffer not NULL terminated");
55
127
  return NULL;
56
171k
}
57
58
static void
59
fu_fdt_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
60
0
{
61
0
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
62
0
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
63
0
  fu_xmlb_builder_insert_kx(bn, "cpuid", priv->cpuid);
64
0
}
65
66
/**
67
 * fu_fdt_firmware_get_cpuid:
68
 * @self: a #FuFdtFirmware
69
 *
70
 * Gets the CPUID.
71
 *
72
 * Returns: integer
73
 *
74
 * Since: 1.8.2
75
 **/
76
guint32
77
fu_fdt_firmware_get_cpuid(FuFdtFirmware *self)
78
0
{
79
0
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
80
0
  g_return_val_if_fail(FU_IS_FDT_FIRMWARE(self), 0x0);
81
0
  return priv->cpuid;
82
0
}
83
84
/**
85
 * fu_fdt_firmware_set_cpuid:
86
 * @self: a #FuFdtFirmware
87
 * @cpuid: integer value
88
 *
89
 * Sets the CPUID.
90
 *
91
 * Since: 1.8.2
92
 **/
93
void
94
fu_fdt_firmware_set_cpuid(FuFdtFirmware *self, guint32 cpuid)
95
0
{
96
0
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
97
0
  g_return_if_fail(FU_IS_FDT_FIRMWARE(self));
98
0
  priv->cpuid = cpuid;
99
0
}
100
101
/**
102
 * fu_fdt_firmware_get_image_by_path:
103
 * @self: a #FuFdtFirmware
104
 * @path: ID path, e.g. `/images/firmware-1`
105
 * @error: (nullable): optional return location for an error
106
 *
107
 * Gets the FDT image for a specific path.
108
 *
109
 * Returns: (transfer full): a #FuFirmware, or %NULL
110
 *
111
 * Since: 1.8.2
112
 **/
113
FuFdtImage *
114
fu_fdt_firmware_get_image_by_path(FuFdtFirmware *self, const gchar *path, GError **error)
115
0
{
116
0
  g_auto(GStrv) paths = NULL;
117
0
  g_autoptr(FuFirmware) img_current = g_object_ref(FU_FIRMWARE(self));
118
119
0
  g_return_val_if_fail(FU_IS_FDT_FIRMWARE(self), NULL);
120
0
  g_return_val_if_fail(path != NULL, NULL);
121
0
  g_return_val_if_fail(path[0] != '\0', NULL);
122
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
123
124
0
  paths = g_strsplit(path, "/", -1);
125
0
  for (guint i = 0; paths[i] != NULL; i++) {
126
0
    const gchar *id = paths[i];
127
0
    g_autoptr(FuFirmware) img_tmp = NULL;
128
129
    /* special case for empty */
130
0
    if (id[0] == '\0')
131
0
      id = NULL;
132
0
    img_tmp = fu_firmware_get_image_by_id(img_current, id, error);
133
0
    if (img_tmp == NULL)
134
0
      return NULL;
135
0
    g_set_object(&img_current, img_tmp);
136
0
  }
137
138
  /* success */
139
0
  return FU_FDT_IMAGE(g_steal_pointer(&img_current));
140
0
}
141
142
static gboolean
143
fu_fdt_firmware_parse_dt_struct(FuFdtFirmware *self, GBytes *fw, GByteArray *strtab, GError **error)
144
3.17k
{
145
3.17k
  gsize bufsz = 0;
146
3.17k
  gsize offset = 0;
147
3.17k
  guint depth = 0;
148
3.17k
  gboolean has_end = FALSE;
149
3.17k
  const guint8 *buf = g_bytes_get_data(fw, &bufsz);
150
3.17k
  g_autoptr(FuFirmware) firmware_current = g_object_ref(FU_FIRMWARE(self));
151
152
  /* debug */
153
3.17k
  fu_dump_bytes(G_LOG_DOMAIN, "dt_struct", fw);
154
155
  /* parse */
156
294k
  while (offset < bufsz) {
157
292k
    guint32 token = 0;
158
159
    /* read tag from aligned offset */
160
292k
    offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4);
161
292k
    if (!fu_memread_uint32_safe(buf, bufsz, offset, &token, G_BIG_ENDIAN, error))
162
153
      return FALSE;
163
292k
    offset += sizeof(guint32);
164
165
    /* nothing to do */
166
292k
    if (token == FU_FDT_TOKEN_NOP)
167
1.99k
      continue;
168
169
    /* END */
170
290k
    if (token == FU_FDT_TOKEN_END) {
171
531
      if (firmware_current != FU_FIRMWARE(self)) {
172
3
        g_set_error_literal(error,
173
3
                FWUPD_ERROR,
174
3
                FWUPD_ERROR_INVALID_DATA,
175
3
                "got END with unclosed node");
176
3
        return FALSE;
177
3
      }
178
528
      has_end = TRUE;
179
528
      break;
180
531
    }
181
182
    /* BEGIN NODE */
183
290k
    if (token == FU_FDT_TOKEN_BEGIN_NODE) {
184
130k
      g_autoptr(GString) str = NULL;
185
130k
      g_autoptr(FuFirmware) image = NULL;
186
187
      /* sanity check */
188
130k
      if (depth++ > FDT_DEPTH_MAX) {
189
0
        g_set_error(error,
190
0
              FWUPD_ERROR,
191
0
              FWUPD_ERROR_INVALID_DATA,
192
0
              "node depth exceeded maximum: 0x%x",
193
0
              (guint)FDT_DEPTH_MAX);
194
0
        return FALSE;
195
0
      }
196
197
130k
      str = fu_fdt_firmware_string_new_safe(buf, bufsz, offset, error);
198
130k
      if (str == NULL)
199
13
        return FALSE;
200
130k
      offset += str->len + 1;
201
130k
      image = fu_fdt_image_new();
202
130k
      if (str->len > 0)
203
122k
        fu_firmware_set_id(image, str->str);
204
130k
      fu_firmware_set_offset(image, offset);
205
130k
      if (!fu_firmware_add_image_full(firmware_current, image, error))
206
28
        return FALSE;
207
130k
      g_set_object(&firmware_current, image);
208
130k
      continue;
209
130k
    }
210
211
    /* END NODE */
212
159k
    if (token == FU_FDT_TOKEN_END_NODE) {
213
118k
      if (firmware_current == FU_FIRMWARE(self)) {
214
2
        g_set_error_literal(error,
215
2
                FWUPD_ERROR,
216
2
                FWUPD_ERROR_INVALID_DATA,
217
2
                "got END NODE with no node to end");
218
2
        return FALSE;
219
2
      }
220
118k
      g_set_object(&firmware_current, fu_firmware_get_parent(firmware_current));
221
118k
      if (depth > 0)
222
118k
        depth--;
223
118k
      continue;
224
118k
    }
225
226
    /* PROP */
227
40.8k
    if (token == FU_FDT_TOKEN_PROP) {
228
40.3k
      guint32 prop_len;
229
40.3k
      guint32 prop_nameoff;
230
40.3k
      g_autoptr(GBytes) blob = NULL;
231
40.3k
      g_autoptr(GString) str = NULL;
232
40.3k
      g_autoptr(GByteArray) st_prp = NULL;
233
234
      /* sanity check */
235
40.3k
      if (firmware_current == FU_FIRMWARE(self)) {
236
4
        g_set_error_literal(error,
237
4
                FWUPD_ERROR,
238
4
                FWUPD_ERROR_INVALID_DATA,
239
4
                "got PROP with unopen node");
240
4
        return FALSE;
241
4
      }
242
243
      /* parse */
244
40.3k
      st_prp = fu_struct_fdt_prop_parse(buf, bufsz, offset, error);
245
40.3k
      if (st_prp == NULL)
246
13
        return FALSE;
247
40.2k
      prop_len = fu_struct_fdt_prop_get_len(st_prp);
248
40.2k
      prop_nameoff = fu_struct_fdt_prop_get_nameoff(st_prp);
249
40.2k
      offset += st_prp->len;
250
251
      /* add property */
252
40.2k
      str = fu_fdt_firmware_string_new_safe(strtab->data,
253
40.2k
                    strtab->len,
254
40.2k
                    prop_nameoff,
255
40.2k
                    error);
256
40.2k
      if (str == NULL) {
257
114
        g_prefix_error(error, "invalid strtab offset 0x%x: ", prop_nameoff);
258
114
        return FALSE;
259
114
      }
260
40.1k
      blob = fu_bytes_new_offset(fw, offset, prop_len, error);
261
40.1k
      if (blob == NULL)
262
130
        return FALSE;
263
40.0k
      fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current), str->str, blob);
264
40.0k
      offset += prop_len;
265
40.0k
      continue;
266
40.1k
    }
267
268
    /* unknown token */
269
553
    g_set_error(error,
270
553
          FWUPD_ERROR,
271
553
          FWUPD_ERROR_INVALID_DATA,
272
553
          "invalid token 0x%x @0%x",
273
553
          token,
274
553
          (guint)offset);
275
553
    return FALSE;
276
40.8k
  }
277
278
  /* did not see FDT_END */
279
2.15k
  if (!has_end)
280
1.63k
    g_warning("did not see FDT_END, perhaps size_dt_struct is invalid?");
281
282
  /* success */
283
2.15k
  return TRUE;
284
3.17k
}
285
286
static gboolean
287
fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self,
288
         GInputStream *stream,
289
         gsize offset,
290
         GError **error)
291
3.59k
{
292
3.59k
  gsize streamsz = 0;
293
294
  /* parse */
295
3.59k
  if (!fu_input_stream_size(stream, &streamsz, error))
296
0
    return FALSE;
297
567k
  for (; offset < streamsz; offset += FU_STRUCT_FDT_RESERVE_ENTRY_SIZE) {
298
564k
    guint64 address = 0;
299
564k
    guint64 size = 0;
300
564k
    g_autoptr(GByteArray) st_res = NULL;
301
564k
    st_res = fu_struct_fdt_reserve_entry_parse_stream(stream, offset, error);
302
564k
    if (st_res == NULL)
303
229
      return FALSE;
304
564k
    address = fu_struct_fdt_reserve_entry_get_address(st_res);
305
564k
    size = fu_struct_fdt_reserve_entry_get_size(st_res);
306
564k
    g_debug("mem_rsvmap: 0x%x, 0x%x", (guint)address, (guint)size);
307
564k
    if (address == 0x0 && size == 0x0)
308
84
      break;
309
564k
  }
310
311
  /* success */
312
3.36k
  return TRUE;
313
3.59k
}
314
315
static gboolean
316
fu_fdt_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
317
12.2M
{
318
12.2M
  return fu_struct_fdt_validate_stream(stream, offset, error);
319
12.2M
}
320
321
static gboolean
322
fu_fdt_firmware_parse(FuFirmware *firmware,
323
          GInputStream *stream,
324
          FuFirmwareParseFlags flags,
325
          GError **error)
326
3.92k
{
327
3.92k
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
328
3.92k
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
329
3.92k
  guint32 totalsize;
330
3.92k
  gsize streamsz = 0;
331
3.92k
  guint32 off_mem_rsvmap = 0;
332
3.92k
  g_autoptr(GByteArray) st_hdr = NULL;
333
334
  /* sanity check */
335
3.92k
  st_hdr = fu_struct_fdt_parse_stream(stream, 0x0, error);
336
3.92k
  if (st_hdr == NULL)
337
0
    return FALSE;
338
3.92k
  if (!fu_input_stream_size(stream, &streamsz, error))
339
0
    return FALSE;
340
3.92k
  totalsize = fu_struct_fdt_get_totalsize(st_hdr);
341
3.92k
  if (totalsize > streamsz) {
342
75
    g_set_error(error,
343
75
          FWUPD_ERROR,
344
75
          FWUPD_ERROR_INVALID_DATA,
345
75
          "truncated image, got 0x%x, expected >= 0x%x",
346
75
          (guint)streamsz,
347
75
          (guint)totalsize);
348
75
    return FALSE;
349
75
  }
350
3.85k
  fu_firmware_set_size(firmware, totalsize);
351
352
  /* read header */
353
3.85k
  priv->cpuid = fu_struct_fdt_get_boot_cpuid_phys(st_hdr);
354
3.85k
  off_mem_rsvmap = fu_struct_fdt_get_off_mem_rsvmap(st_hdr);
355
3.85k
  if (off_mem_rsvmap != 0x0) {
356
3.59k
    if (!fu_fdt_firmware_parse_mem_rsvmap(self, stream, off_mem_rsvmap, error))
357
229
      return FALSE;
358
3.59k
  }
359
3.62k
  if (fu_struct_fdt_get_last_comp_version(st_hdr) < FDT_LAST_COMP_VERSION) {
360
31
    g_set_error(error,
361
31
          FWUPD_ERROR,
362
31
          FWUPD_ERROR_INVALID_DATA,
363
31
          "invalid header version, got 0x%x, expected >= 0x%x",
364
31
          (guint)fu_struct_fdt_get_last_comp_version(st_hdr),
365
31
          (guint)FDT_LAST_COMP_VERSION);
366
31
    return FALSE;
367
31
  }
368
3.59k
  fu_firmware_set_version_raw(firmware, fu_struct_fdt_get_version(st_hdr));
369
370
  /* parse device tree struct */
371
3.59k
  if (fu_struct_fdt_get_size_dt_struct(st_hdr) != 0x0 &&
372
3.59k
      fu_struct_fdt_get_size_dt_strings(st_hdr) != 0x0) {
373
3.57k
    g_autoptr(GByteArray) dt_strings = NULL;
374
3.57k
    g_autoptr(GByteArray) dt_struct = NULL;
375
3.57k
    g_autoptr(GBytes) dt_struct_buf = NULL;
376
3.57k
    dt_strings =
377
3.57k
        fu_input_stream_read_byte_array(stream,
378
3.57k
                fu_struct_fdt_get_off_dt_strings(st_hdr),
379
3.57k
                fu_struct_fdt_get_size_dt_strings(st_hdr),
380
3.57k
                NULL,
381
3.57k
                error);
382
3.57k
    if (dt_strings == NULL)
383
147
      return FALSE;
384
3.42k
    dt_struct =
385
3.42k
        fu_input_stream_read_byte_array(stream,
386
3.42k
                fu_struct_fdt_get_off_dt_struct(st_hdr),
387
3.42k
                fu_struct_fdt_get_size_dt_struct(st_hdr),
388
3.42k
                NULL,
389
3.42k
                error);
390
3.42k
    if (dt_struct == NULL)
391
56
      return FALSE;
392
3.37k
    if (dt_struct->len != fu_struct_fdt_get_size_dt_struct(st_hdr)) {
393
201
      g_set_error(error,
394
201
            FWUPD_ERROR,
395
201
            FWUPD_ERROR_INVALID_DATA,
396
201
            "invalid firmware -- dt_struct invalid");
397
201
      return FALSE;
398
201
    }
399
3.17k
    dt_struct_buf =
400
3.17k
        g_byte_array_free_to_bytes(g_steal_pointer(&dt_struct)); /* nocheck:blocked */
401
3.17k
    if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct_buf, dt_strings, error))
402
1.01k
      return FALSE;
403
3.17k
  }
404
405
  /* success */
406
2.17k
  return TRUE;
407
3.59k
}
408
409
typedef struct {
410
  GByteArray *dt_strings;
411
  GByteArray *dt_struct;
412
  GHashTable *strtab;
413
} FuFdtFirmwareBuildHelper;
414
415
static guint32
416
fu_fdt_firmware_append_to_strtab(FuFdtFirmwareBuildHelper *helper, const gchar *key)
417
15.5k
{
418
15.5k
  gpointer tmp = NULL;
419
15.5k
  guint32 offset;
420
421
  /* already exists */
422
15.5k
  if (g_hash_table_lookup_extended(helper->strtab, key, NULL, &tmp))
423
5.78k
    return GPOINTER_TO_UINT(tmp);
424
425
9.78k
  g_debug("adding strtab: %s", key);
426
9.78k
  offset = helper->dt_strings->len;
427
9.78k
  g_byte_array_append(helper->dt_strings, (const guint8 *)key, strlen(key));
428
9.78k
  fu_byte_array_append_uint8(helper->dt_strings, 0x0);
429
9.78k
  g_hash_table_insert(helper->strtab, g_strdup(key), GUINT_TO_POINTER(offset));
430
9.78k
  return offset;
431
15.5k
}
432
433
static gboolean
434
fu_fdt_firmware_write_image(FuFdtFirmware *self,
435
          FuFdtImage *img,
436
          FuFdtFirmwareBuildHelper *helper,
437
          guint depth,
438
          GError **error)
439
9.46k
{
440
9.46k
  const gchar *id = fu_firmware_get_id(FU_FIRMWARE(img));
441
9.46k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(FU_FIRMWARE(img));
442
9.46k
  g_autoptr(GPtrArray) attrs = fu_fdt_image_get_attrs(img);
443
444
  /* sanity check */
445
9.46k
  if (depth > 0 && id == NULL) {
446
127
    g_set_error_literal(error,
447
127
            FWUPD_ERROR,
448
127
            FWUPD_ERROR_INVALID_DATA,
449
127
            "child FuFdtImage requires ID");
450
127
    return FALSE;
451
127
  }
452
453
  /* BEGIN_NODE, ID, NUL */
454
9.33k
  fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_BEGIN_NODE, G_BIG_ENDIAN);
455
9.33k
  if (id != NULL) {
456
8.19k
    g_byte_array_append(helper->dt_struct, (const guint8 *)id, strlen(id) + 1);
457
8.19k
  } else {
458
1.14k
    fu_byte_array_append_uint8(helper->dt_struct, 0x0);
459
1.14k
  }
460
9.33k
  fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0);
461
462
  /* write properties */
463
24.9k
  for (guint i = 0; i < attrs->len; i++) {
464
15.5k
    const gchar *key = g_ptr_array_index(attrs, i);
465
15.5k
    g_autoptr(GBytes) blob = NULL;
466
15.5k
    g_autoptr(GByteArray) st_prp = fu_struct_fdt_prop_new();
467
468
15.5k
    blob = fu_fdt_image_get_attr(img, key, error);
469
15.5k
    if (blob == NULL)
470
0
      return FALSE;
471
15.5k
    fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_PROP, G_BIG_ENDIAN);
472
15.5k
    fu_struct_fdt_prop_set_len(st_prp, g_bytes_get_size(blob));
473
15.5k
    fu_struct_fdt_prop_set_nameoff(st_prp,
474
15.5k
                 fu_fdt_firmware_append_to_strtab(helper, key));
475
15.5k
    g_byte_array_append(helper->dt_struct, st_prp->data, st_prp->len);
476
15.5k
    fu_byte_array_append_bytes(helper->dt_struct, blob);
477
15.5k
    fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0);
478
15.5k
  }
479
480
  /* write children, recursively */
481
16.6k
  for (guint i = 0; i < images->len; i++) {
482
8.04k
    FuFdtImage *img_child = g_ptr_array_index(images, i);
483
8.04k
    if (!fu_fdt_firmware_write_image(self, img_child, helper, depth + 1, error))
484
704
      return FALSE;
485
8.04k
  }
486
487
  /* END_NODE */
488
8.63k
  fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_END_NODE, G_BIG_ENDIAN);
489
8.63k
  return TRUE;
490
9.33k
}
491
492
static GByteArray *
493
fu_fdt_firmware_write(FuFirmware *firmware, GError **error)
494
1.44k
{
495
1.44k
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
496
1.44k
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
497
1.44k
  guint32 off_dt_struct;
498
1.44k
  guint32 off_dt_strings;
499
1.44k
  guint32 off_mem_rsvmap;
500
1.44k
  g_autoptr(GByteArray) dt_strings = g_byte_array_new();
501
1.44k
  g_autoptr(GByteArray) dt_struct = g_byte_array_new();
502
1.44k
  g_autoptr(GHashTable) strtab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
503
1.44k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
504
1.44k
  g_autoptr(GByteArray) st_hdr = fu_struct_fdt_new();
505
1.44k
  g_autoptr(GByteArray) mem_rsvmap = fu_struct_fdt_reserve_entry_new();
506
1.44k
  FuFdtFirmwareBuildHelper helper = {
507
1.44k
      .dt_strings = dt_strings,
508
1.44k
      .dt_struct = dt_struct,
509
1.44k
      .strtab = strtab,
510
1.44k
  };
511
512
  /* empty mem_rsvmap */
513
1.44k
  off_mem_rsvmap = fu_common_align_up(st_hdr->len, FU_FIRMWARE_ALIGNMENT_4);
514
515
  /* dt_struct */
516
1.44k
  off_dt_struct =
517
1.44k
      fu_common_align_up(off_mem_rsvmap + mem_rsvmap->len, FU_FIRMWARE_ALIGNMENT_4);
518
519
  /* only one root node supported */
520
1.44k
  if (images->len != 1) {
521
23
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "no root node");
522
23
    return NULL;
523
23
  }
524
1.42k
  if (!fu_fdt_firmware_write_image(self,
525
1.42k
           FU_FDT_IMAGE(g_ptr_array_index(images, 0)),
526
1.42k
           &helper,
527
1.42k
           0,
528
1.42k
           error))
529
127
    return NULL;
530
1.29k
  fu_byte_array_append_uint32(dt_struct, FU_FDT_TOKEN_END, G_BIG_ENDIAN);
531
532
  /* dt_strings */
533
1.29k
  off_dt_strings =
534
1.29k
      fu_common_align_up(off_dt_struct + dt_struct->len, FU_FIRMWARE_ALIGNMENT_4);
535
536
  /* write header */
537
1.29k
  fu_struct_fdt_set_totalsize(st_hdr, off_dt_strings + dt_strings->len);
538
1.29k
  fu_struct_fdt_set_off_dt_struct(st_hdr, off_dt_struct);
539
1.29k
  fu_struct_fdt_set_off_dt_strings(st_hdr, off_dt_strings);
540
1.29k
  fu_struct_fdt_set_off_mem_rsvmap(st_hdr, off_mem_rsvmap);
541
1.29k
  fu_struct_fdt_set_version(st_hdr, fu_firmware_get_version_raw(firmware));
542
1.29k
  fu_struct_fdt_set_boot_cpuid_phys(st_hdr, priv->cpuid);
543
1.29k
  fu_struct_fdt_set_size_dt_strings(st_hdr, dt_strings->len);
544
1.29k
  fu_struct_fdt_set_size_dt_struct(st_hdr, dt_struct->len);
545
1.29k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
546
547
  /* write mem_rsvmap, dt_struct, dt_strings */
548
1.29k
  g_byte_array_append(st_hdr, mem_rsvmap->data, mem_rsvmap->len);
549
1.29k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
550
1.29k
  g_byte_array_append(st_hdr, dt_struct->data, dt_struct->len);
551
1.29k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
552
1.29k
  g_byte_array_append(st_hdr, dt_strings->data, dt_strings->len);
553
1.29k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
554
555
  /* success */
556
1.29k
  return g_steal_pointer(&st_hdr);
557
1.42k
}
558
559
static gboolean
560
fu_fdt_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
561
0
{
562
0
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
563
0
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
564
0
  guint64 tmp;
565
566
  /* optional properties */
567
0
  tmp = xb_node_query_text_as_uint(n, "cpuid", NULL);
568
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
569
0
    priv->cpuid = tmp;
570
571
  /* success */
572
0
  return TRUE;
573
0
}
574
575
static void
576
fu_fdt_firmware_init(FuFdtFirmware *self)
577
4.18k
{
578
4.18k
  g_type_ensure(FU_TYPE_FDT_IMAGE);
579
4.18k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
580
4.18k
}
581
582
static void
583
fu_fdt_firmware_class_init(FuFdtFirmwareClass *klass)
584
2
{
585
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
586
2
  firmware_class->validate = fu_fdt_firmware_validate;
587
2
  firmware_class->export = fu_fdt_firmware_export;
588
2
  firmware_class->parse = fu_fdt_firmware_parse;
589
2
  firmware_class->write = fu_fdt_firmware_write;
590
2
  firmware_class->build = fu_fdt_firmware_build;
591
2
}
592
593
/**
594
 * fu_fdt_firmware_new:
595
 *
596
 * Creates a new #FuFirmware of sub type FDT
597
 *
598
 * Since: 1.8.2
599
 **/
600
FuFirmware *
601
fu_fdt_firmware_new(void)
602
0
{
603
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_FDT_FIRMWARE, NULL));
604
0
}