Coverage Report

Created: 2025-08-26 06:55

/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
500k
#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.53k
#define GET_PRIVATE(o) (fu_fdt_firmware_get_instance_private(o))
38
39
3.83k
#define FDT_LAST_COMP_VERSION 2
40
158k
#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
199k
{
45
199k
  g_autoptr(GString) str = g_string_new(NULL);
46
1.84G
  for (gsize i = offset; i < bufsz; i++) {
47
1.84G
    if (buf[i] == '\0')
48
199k
      return g_steal_pointer(&str);
49
1.84G
    g_string_append_c(str, (gchar)buf[i]);
50
1.84G
  }
51
163
  g_set_error_literal(error,
52
163
          FWUPD_ERROR,
53
163
          FWUPD_ERROR_INVALID_DATA,
54
163
          "buffer not NULL terminated");
55
163
  return NULL;
56
199k
}
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.27k
{
145
3.27k
  gsize bufsz = 0;
146
3.27k
  gsize offset = 0;
147
3.27k
  guint depth = 0;
148
3.27k
  gboolean has_end = FALSE;
149
3.27k
  const guint8 *buf = g_bytes_get_data(fw, &bufsz);
150
3.27k
  g_autoptr(FuFirmware) firmware_current = g_object_ref(FU_FIRMWARE(self));
151
152
  /* debug */
153
3.27k
  fu_dump_bytes(G_LOG_DOMAIN, "dt_struct", fw);
154
155
  /* parse */
156
350k
  while (offset < bufsz) {
157
348k
    guint32 token = 0;
158
159
    /* read tag from aligned offset */
160
348k
    offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4);
161
348k
    if (!fu_memread_uint32_safe(buf, bufsz, offset, &token, G_BIG_ENDIAN, error))
162
213
      return FALSE;
163
348k
    offset += sizeof(guint32);
164
165
    /* nothing to do */
166
348k
    if (token == FU_FDT_TOKEN_NOP)
167
2.07k
      continue;
168
169
    /* END */
170
346k
    if (token == FU_FDT_TOKEN_END) {
171
455
      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
452
      has_end = TRUE;
179
452
      break;
180
455
    }
181
182
    /* BEGIN NODE */
183
346k
    if (token == FU_FDT_TOKEN_BEGIN_NODE) {
184
158k
      g_autoptr(GString) str = NULL;
185
158k
      g_autoptr(FuFirmware) image = NULL;
186
187
      /* sanity check */
188
158k
      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
158k
      str = fu_fdt_firmware_string_new_safe(buf, bufsz, offset, error);
198
158k
      if (str == NULL)
199
14
        return FALSE;
200
158k
      offset += str->len + 1;
201
158k
      image = fu_fdt_image_new();
202
158k
      if (str->len > 0)
203
150k
        fu_firmware_set_id(image, str->str);
204
158k
      fu_firmware_set_offset(image, offset);
205
158k
      if (!fu_firmware_add_image_full(firmware_current, image, error))
206
29
        return FALSE;
207
158k
      g_set_object(&firmware_current, image);
208
158k
      continue;
209
158k
    }
210
211
    /* END NODE */
212
187k
    if (token == FU_FDT_TOKEN_END_NODE) {
213
146k
      if (firmware_current == FU_FIRMWARE(self)) {
214
3
        g_set_error_literal(error,
215
3
                FWUPD_ERROR,
216
3
                FWUPD_ERROR_INVALID_DATA,
217
3
                "got END NODE with no node to end");
218
3
        return FALSE;
219
3
      }
220
146k
      g_set_object(&firmware_current, fu_firmware_get_parent(firmware_current));
221
146k
      if (depth > 0)
222
146k
        depth--;
223
146k
      continue;
224
146k
    }
225
226
    /* PROP */
227
40.7k
    if (token == FU_FDT_TOKEN_PROP) {
228
40.2k
      guint32 prop_len;
229
40.2k
      guint32 prop_nameoff;
230
40.2k
      g_autoptr(GBytes) blob = NULL;
231
40.2k
      g_autoptr(GString) str = NULL;
232
40.2k
      g_autoptr(GByteArray) st_prp = NULL;
233
234
      /* sanity check */
235
40.2k
      if (firmware_current == FU_FIRMWARE(self)) {
236
5
        g_set_error_literal(error,
237
5
                FWUPD_ERROR,
238
5
                FWUPD_ERROR_INVALID_DATA,
239
5
                "got PROP with unopen node");
240
5
        return FALSE;
241
5
      }
242
243
      /* parse */
244
40.2k
      st_prp = fu_struct_fdt_prop_parse(buf, bufsz, offset, error);
245
40.2k
      if (st_prp == NULL)
246
15
        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
149
        g_prefix_error(error, "invalid strtab offset 0x%x: ", prop_nameoff);
258
149
        return FALSE;
259
149
      }
260
40.0k
      blob = fu_bytes_new_offset(fw, offset, prop_len, error);
261
40.0k
      if (blob == NULL)
262
159
        return FALSE;
263
39.8k
      fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current), str->str, blob);
264
39.8k
      offset += prop_len;
265
39.8k
      continue;
266
40.0k
    }
267
268
    /* unknown token */
269
532
    g_set_error(error,
270
532
          FWUPD_ERROR,
271
532
          FWUPD_ERROR_INVALID_DATA,
272
532
          "invalid token 0x%x @0%x",
273
532
          token,
274
532
          (guint)offset);
275
532
    return FALSE;
276
40.7k
  }
277
278
  /* did not see FDT_END */
279
2.15k
  if (!has_end)
280
1.70k
    g_warning("did not see FDT_END, perhaps size_dt_struct is invalid?");
281
282
  /* success */
283
2.15k
  return TRUE;
284
3.27k
}
285
286
static gboolean
287
fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self,
288
         GInputStream *stream,
289
         gsize offset,
290
         GError **error)
291
3.75k
{
292
3.75k
  gsize streamsz = 0;
293
294
  /* parse */
295
3.75k
  if (!fu_input_stream_size(stream, &streamsz, error))
296
0
    return FALSE;
297
489k
  for (; offset < streamsz; offset += FU_STRUCT_FDT_RESERVE_ENTRY_SIZE) {
298
486k
    guint64 address = 0;
299
486k
    guint64 size = 0;
300
486k
    g_autoptr(GByteArray) st_res = NULL;
301
486k
    st_res = fu_struct_fdt_reserve_entry_parse_stream(stream, offset, error);
302
486k
    if (st_res == NULL)
303
229
      return FALSE;
304
486k
    address = fu_struct_fdt_reserve_entry_get_address(st_res);
305
486k
    size = fu_struct_fdt_reserve_entry_get_size(st_res);
306
486k
    g_debug("mem_rsvmap: 0x%x, 0x%x", (guint)address, (guint)size);
307
486k
    if (address == 0x0 && size == 0x0)
308
91
      break;
309
486k
  }
310
311
  /* success */
312
3.52k
  return TRUE;
313
3.75k
}
314
315
static gboolean
316
fu_fdt_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
317
16.3M
{
318
16.3M
  return fu_struct_fdt_validate_stream(stream, offset, error);
319
16.3M
}
320
321
static gboolean
322
fu_fdt_firmware_parse(FuFirmware *firmware,
323
          GInputStream *stream,
324
          FuFirmwareParseFlags flags,
325
          GError **error)
326
4.10k
{
327
4.10k
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
328
4.10k
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
329
4.10k
  guint32 totalsize;
330
4.10k
  gsize streamsz = 0;
331
4.10k
  guint32 off_mem_rsvmap = 0;
332
4.10k
  g_autoptr(GByteArray) st_hdr = NULL;
333
334
  /* sanity check */
335
4.10k
  st_hdr = fu_struct_fdt_parse_stream(stream, 0x0, error);
336
4.10k
  if (st_hdr == NULL)
337
0
    return FALSE;
338
4.10k
  if (!fu_input_stream_size(stream, &streamsz, error))
339
0
    return FALSE;
340
4.10k
  totalsize = fu_struct_fdt_get_totalsize(st_hdr);
341
4.10k
  if (totalsize > streamsz) {
342
70
    g_set_error(error,
343
70
          FWUPD_ERROR,
344
70
          FWUPD_ERROR_INVALID_DATA,
345
70
          "truncated image, got 0x%x, expected >= 0x%x",
346
70
          (guint)streamsz,
347
70
          (guint)totalsize);
348
70
    return FALSE;
349
70
  }
350
4.03k
  fu_firmware_set_size(firmware, totalsize);
351
352
  /* read header */
353
4.03k
  priv->cpuid = fu_struct_fdt_get_boot_cpuid_phys(st_hdr);
354
4.03k
  off_mem_rsvmap = fu_struct_fdt_get_off_mem_rsvmap(st_hdr);
355
4.03k
  if (off_mem_rsvmap != 0x0) {
356
3.75k
    if (!fu_fdt_firmware_parse_mem_rsvmap(self, stream, off_mem_rsvmap, error))
357
229
      return FALSE;
358
3.75k
  }
359
3.80k
  if (fu_struct_fdt_get_last_comp_version(st_hdr) < FDT_LAST_COMP_VERSION) {
360
33
    g_set_error(error,
361
33
          FWUPD_ERROR,
362
33
          FWUPD_ERROR_INVALID_DATA,
363
33
          "invalid header version, got 0x%x, expected >= 0x%x",
364
33
          (guint)fu_struct_fdt_get_last_comp_version(st_hdr),
365
33
          (guint)FDT_LAST_COMP_VERSION);
366
33
    return FALSE;
367
33
  }
368
3.76k
  fu_firmware_set_version_raw(firmware, fu_struct_fdt_get_version(st_hdr));
369
370
  /* parse device tree struct */
371
3.76k
  if (fu_struct_fdt_get_size_dt_struct(st_hdr) != 0x0 &&
372
3.76k
      fu_struct_fdt_get_size_dt_strings(st_hdr) != 0x0) {
373
3.74k
    g_autoptr(GByteArray) dt_strings = NULL;
374
3.74k
    g_autoptr(GByteArray) dt_struct = NULL;
375
3.74k
    g_autoptr(GBytes) dt_struct_buf = NULL;
376
3.74k
    dt_strings =
377
3.74k
        fu_input_stream_read_byte_array(stream,
378
3.74k
                fu_struct_fdt_get_off_dt_strings(st_hdr),
379
3.74k
                fu_struct_fdt_get_size_dt_strings(st_hdr),
380
3.74k
                NULL,
381
3.74k
                error);
382
3.74k
    if (dt_strings == NULL)
383
177
      return FALSE;
384
3.56k
    dt_struct =
385
3.56k
        fu_input_stream_read_byte_array(stream,
386
3.56k
                fu_struct_fdt_get_off_dt_struct(st_hdr),
387
3.56k
                fu_struct_fdt_get_size_dt_struct(st_hdr),
388
3.56k
                NULL,
389
3.56k
                error);
390
3.56k
    if (dt_struct == NULL)
391
73
      return FALSE;
392
3.49k
    if (dt_struct->len != fu_struct_fdt_get_size_dt_struct(st_hdr)) {
393
222
      g_set_error_literal(error,
394
222
              FWUPD_ERROR,
395
222
              FWUPD_ERROR_INVALID_DATA,
396
222
              "invalid firmware -- dt_struct invalid");
397
222
      return FALSE;
398
222
    }
399
3.27k
    dt_struct_buf =
400
3.27k
        g_byte_array_free_to_bytes(g_steal_pointer(&dt_struct)); /* nocheck:blocked */
401
3.27k
    if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct_buf, dt_strings, error))
402
1.12k
      return FALSE;
403
3.27k
  }
404
405
  /* success */
406
2.17k
  return TRUE;
407
3.76k
}
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.67k
    return GPOINTER_TO_UINT(tmp);
424
425
9.86k
  g_debug("adding strtab: %s", key);
426
9.86k
  offset = helper->dt_strings->len;
427
9.86k
  g_byte_array_append(helper->dt_strings, (const guint8 *)key, strlen(key));
428
9.86k
  fu_byte_array_append_uint8(helper->dt_strings, 0x0);
429
9.86k
  g_hash_table_insert(helper->strtab, g_strdup(key), GUINT_TO_POINTER(offset));
430
9.86k
  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
8.87k
{
440
8.87k
  const gchar *id = fu_firmware_get_id(FU_FIRMWARE(img));
441
8.87k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(FU_FIRMWARE(img));
442
8.87k
  g_autoptr(GPtrArray) attrs = fu_fdt_image_get_attrs(img);
443
444
  /* sanity check */
445
8.87k
  if (depth > 0 && id == NULL) {
446
131
    g_set_error_literal(error,
447
131
            FWUPD_ERROR,
448
131
            FWUPD_ERROR_INVALID_DATA,
449
131
            "child FuFdtImage requires ID");
450
131
    return FALSE;
451
131
  }
452
453
  /* BEGIN_NODE, ID, NUL */
454
8.74k
  fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_BEGIN_NODE, G_BIG_ENDIAN);
455
8.74k
  if (id != NULL) {
456
7.59k
    g_byte_array_append(helper->dt_struct, (const guint8 *)id, strlen(id) + 1);
457
7.59k
  } else {
458
1.14k
    fu_byte_array_append_uint8(helper->dt_struct, 0x0);
459
1.14k
  }
460
8.74k
  fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0);
461
462
  /* write properties */
463
24.2k
  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
15.4k
  for (guint i = 0; i < images->len; i++) {
482
7.47k
    FuFdtImage *img_child = g_ptr_array_index(images, i);
483
7.47k
    if (!fu_fdt_firmware_write_image(self, img_child, helper, depth + 1, error))
484
763
      return FALSE;
485
7.47k
  }
486
487
  /* END_NODE */
488
7.98k
  fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_END_NODE, G_BIG_ENDIAN);
489
7.98k
  return TRUE;
490
8.74k
}
491
492
static GByteArray *
493
fu_fdt_firmware_write(FuFirmware *firmware, GError **error)
494
1.43k
{
495
1.43k
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
496
1.43k
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
497
1.43k
  guint32 off_dt_struct;
498
1.43k
  guint32 off_dt_strings;
499
1.43k
  guint32 off_mem_rsvmap;
500
1.43k
  g_autoptr(GByteArray) dt_strings = g_byte_array_new();
501
1.43k
  g_autoptr(GByteArray) dt_struct = g_byte_array_new();
502
1.43k
  g_autoptr(GHashTable) strtab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
503
1.43k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
504
1.43k
  g_autoptr(GByteArray) st_hdr = fu_struct_fdt_new();
505
1.43k
  g_autoptr(GByteArray) mem_rsvmap = fu_struct_fdt_reserve_entry_new();
506
1.43k
  FuFdtFirmwareBuildHelper helper = {
507
1.43k
      .dt_strings = dt_strings,
508
1.43k
      .dt_struct = dt_struct,
509
1.43k
      .strtab = strtab,
510
1.43k
  };
511
512
  /* empty mem_rsvmap */
513
1.43k
  off_mem_rsvmap = fu_common_align_up(st_hdr->len, FU_FIRMWARE_ALIGNMENT_4);
514
515
  /* dt_struct */
516
1.43k
  off_dt_struct =
517
1.43k
      fu_common_align_up(off_mem_rsvmap + mem_rsvmap->len, FU_FIRMWARE_ALIGNMENT_4);
518
519
  /* only one root node supported */
520
1.43k
  if (images->len != 1) {
521
31
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "no root node");
522
31
    return NULL;
523
31
  }
524
1.40k
  if (!fu_fdt_firmware_write_image(self,
525
1.40k
           FU_FDT_IMAGE(g_ptr_array_index(images, 0)),
526
1.40k
           &helper,
527
1.40k
           0,
528
1.40k
           error))
529
131
    return NULL;
530
1.27k
  fu_byte_array_append_uint32(dt_struct, FU_FDT_TOKEN_END, G_BIG_ENDIAN);
531
532
  /* dt_strings */
533
1.27k
  off_dt_strings =
534
1.27k
      fu_common_align_up(off_dt_struct + dt_struct->len, FU_FIRMWARE_ALIGNMENT_4);
535
536
  /* write header */
537
1.27k
  fu_struct_fdt_set_totalsize(st_hdr, off_dt_strings + dt_strings->len);
538
1.27k
  fu_struct_fdt_set_off_dt_struct(st_hdr, off_dt_struct);
539
1.27k
  fu_struct_fdt_set_off_dt_strings(st_hdr, off_dt_strings);
540
1.27k
  fu_struct_fdt_set_off_mem_rsvmap(st_hdr, off_mem_rsvmap);
541
1.27k
  fu_struct_fdt_set_version(st_hdr, fu_firmware_get_version_raw(firmware));
542
1.27k
  fu_struct_fdt_set_boot_cpuid_phys(st_hdr, priv->cpuid);
543
1.27k
  fu_struct_fdt_set_size_dt_strings(st_hdr, dt_strings->len);
544
1.27k
  fu_struct_fdt_set_size_dt_struct(st_hdr, dt_struct->len);
545
1.27k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
546
547
  /* write mem_rsvmap, dt_struct, dt_strings */
548
1.27k
  g_byte_array_append(st_hdr, mem_rsvmap->data, mem_rsvmap->len);
549
1.27k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
550
1.27k
  g_byte_array_append(st_hdr, dt_struct->data, dt_struct->len);
551
1.27k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
552
1.27k
  g_byte_array_append(st_hdr, dt_strings->data, dt_strings->len);
553
1.27k
  fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0);
554
555
  /* success */
556
1.27k
  return g_steal_pointer(&st_hdr);
557
1.40k
}
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.36k
{
578
4.36k
  g_type_ensure(FU_TYPE_FDT_IMAGE);
579
4.36k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
580
4.36k
}
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
}