Coverage Report

Created: 2026-01-25 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-fdt-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
243k
#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
6.30k
G_DEFINE_TYPE_WITH_PRIVATE(FuFdtFirmware, fu_fdt_firmware, FU_TYPE_FIRMWARE)
37
6.30k
#define GET_PRIVATE(o) (fu_fdt_firmware_get_instance_private(o))
38
39
3.22k
#define FDT_LAST_COMP_VERSION 2
40
91.7k
#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
109k
{
45
109k
  g_autoptr(GString) str = g_string_new(NULL);
46
548M
  for (gsize i = offset; i < bufsz; i++) {
47
548M
    if (buf[i] == '\0')
48
109k
      return g_steal_pointer(&str);
49
548M
    g_string_append_c(str, (gchar)buf[i]);
50
548M
  }
51
130
  g_set_error_literal(error,
52
130
          FWUPD_ERROR,
53
130
          FWUPD_ERROR_INVALID_DATA,
54
130
          "buffer not NULL terminated");
55
130
  return NULL;
56
109k
}
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
2.82k
{
145
2.82k
  gsize bufsz = 0;
146
2.82k
  gsize offset = 0;
147
2.82k
  guint depth = 0;
148
2.82k
  gboolean has_end = FALSE;
149
2.82k
  const guint8 *buf = g_bytes_get_data(fw, &bufsz);
150
2.82k
  g_autoptr(FuFirmware) firmware_current = g_object_ref(FU_FIRMWARE(self));
151
152
  /* parse */
153
194k
  while (offset < bufsz) {
154
192k
    guint32 token = 0;
155
156
    /* read tag from aligned offset */
157
192k
    offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4);
158
192k
    if (offset > G_MAXUINT32) {
159
0
      g_set_error_literal(error,
160
0
              FWUPD_ERROR,
161
0
              FWUPD_ERROR_INVALID_DATA,
162
0
              "offset bigger than 4GB");
163
0
      return FALSE;
164
0
    }
165
192k
    if (!fu_memread_uint32_safe(buf, bufsz, offset, &token, G_BIG_ENDIAN, error))
166
210
      return FALSE;
167
192k
    offset += sizeof(guint32);
168
169
    /* nothing to do */
170
192k
    if (token == FU_FDT_TOKEN_NOP)
171
1.41k
      continue;
172
173
    /* END */
174
191k
    if (token == FU_FDT_TOKEN_END) {
175
230
      if (firmware_current != FU_FIRMWARE(self)) {
176
3
        g_set_error_literal(error,
177
3
                FWUPD_ERROR,
178
3
                FWUPD_ERROR_INVALID_DATA,
179
3
                "got END with unclosed node");
180
3
        return FALSE;
181
3
      }
182
227
      has_end = TRUE;
183
227
      break;
184
230
    }
185
186
    /* BEGIN NODE */
187
191k
    if (token == FU_FDT_TOKEN_BEGIN_NODE) {
188
91.7k
      g_autoptr(GString) str = NULL;
189
91.7k
      g_autoptr(FuFirmware) image = NULL;
190
191
      /* sanity check */
192
91.7k
      if (depth++ > FDT_DEPTH_MAX) {
193
0
        g_set_error(error,
194
0
              FWUPD_ERROR,
195
0
              FWUPD_ERROR_INVALID_DATA,
196
0
              "node depth exceeded maximum: 0x%x",
197
0
              (guint)FDT_DEPTH_MAX);
198
0
        return FALSE;
199
0
      }
200
201
91.7k
      str = fu_fdt_firmware_string_new_safe(buf, bufsz, offset, error);
202
91.7k
      if (str == NULL)
203
16
        return FALSE;
204
91.7k
      offset += str->len + 1;
205
91.7k
      image = fu_fdt_image_new();
206
91.7k
      if (str->len > 0)
207
86.5k
        fu_firmware_set_id(image, str->str);
208
91.7k
      fu_firmware_set_offset(image, offset);
209
91.7k
      if (!fu_firmware_add_image(firmware_current, image, error))
210
18
        return FALSE;
211
91.7k
      g_set_object(&firmware_current, image);
212
91.7k
      continue;
213
91.7k
    }
214
215
    /* END NODE */
216
99.2k
    if (token == FU_FDT_TOKEN_END_NODE) {
217
81.4k
      if (firmware_current == FU_FIRMWARE(self)) {
218
2
        g_set_error_literal(error,
219
2
                FWUPD_ERROR,
220
2
                FWUPD_ERROR_INVALID_DATA,
221
2
                "got END NODE with no node to end");
222
2
        return FALSE;
223
2
      }
224
81.4k
      g_set_object(&firmware_current, fu_firmware_get_parent(firmware_current));
225
81.4k
      if (depth > 0)
226
81.4k
        depth--;
227
81.4k
      continue;
228
81.4k
    }
229
230
    /* PROP */
231
17.8k
    if (token == FU_FDT_TOKEN_PROP) {
232
17.4k
      guint32 prop_len;
233
17.4k
      guint32 prop_nameoff;
234
17.4k
      g_autoptr(GBytes) blob = NULL;
235
17.4k
      g_autoptr(GString) str = NULL;
236
17.4k
      g_autoptr(FuStructFdtProp) st_prp = NULL;
237
238
      /* sanity check */
239
17.4k
      if (firmware_current == FU_FIRMWARE(self)) {
240
3
        g_set_error_literal(error,
241
3
                FWUPD_ERROR,
242
3
                FWUPD_ERROR_INVALID_DATA,
243
3
                "got PROP with unopen node");
244
3
        return FALSE;
245
3
      }
246
247
      /* parse */
248
17.4k
      st_prp = fu_struct_fdt_prop_parse(buf, bufsz, offset, error);
249
17.4k
      if (st_prp == NULL)
250
13
        return FALSE;
251
17.4k
      prop_len = fu_struct_fdt_prop_get_len(st_prp);
252
17.4k
      prop_nameoff = fu_struct_fdt_prop_get_nameoff(st_prp);
253
17.4k
      offset += st_prp->buf->len;
254
255
      /* add property */
256
17.4k
      str = fu_fdt_firmware_string_new_safe(strtab->data,
257
17.4k
                    strtab->len,
258
17.4k
                    prop_nameoff,
259
17.4k
                    error);
260
17.4k
      if (str == NULL) {
261
114
        g_prefix_error(error, "invalid strtab offset 0x%x: ", prop_nameoff);
262
114
        return FALSE;
263
114
      }
264
17.2k
      blob = fu_bytes_new_offset(fw, offset, prop_len, error);
265
17.2k
      if (blob == NULL)
266
140
        return FALSE;
267
17.1k
      fu_fdt_image_set_attr(FU_FDT_IMAGE(firmware_current), str->str, blob);
268
17.1k
      offset += prop_len;
269
17.1k
      continue;
270
17.2k
    }
271
272
    /* unknown token */
273
439
    g_set_error(error,
274
439
          FWUPD_ERROR,
275
439
          FWUPD_ERROR_INVALID_DATA,
276
439
          "invalid token 0x%x @0%x",
277
439
          token,
278
439
          (guint)offset);
279
439
    return FALSE;
280
17.8k
  }
281
282
  /* did not see FDT_END */
283
1.86k
  if (!has_end)
284
1.63k
    g_warning("did not see FDT_END, perhaps size_dt_struct is invalid?");
285
286
  /* success */
287
1.86k
  return TRUE;
288
2.82k
}
289
290
static gboolean
291
fu_fdt_firmware_parse_mem_rsvmap(FuFdtFirmware *self,
292
         GInputStream *stream,
293
         gsize offset,
294
         GError **error)
295
2.99k
{
296
2.99k
  gsize streamsz = 0;
297
298
  /* parse */
299
2.99k
  if (!fu_input_stream_size(stream, &streamsz, error))
300
0
    return FALSE;
301
238k
  for (; offset < streamsz; offset += FU_STRUCT_FDT_RESERVE_ENTRY_SIZE) {
302
235k
    guint64 address = 0;
303
235k
    guint64 size = 0;
304
235k
    g_autoptr(FuStructFdtReserveEntry) st_res = NULL;
305
235k
    st_res = fu_struct_fdt_reserve_entry_parse_stream(stream, offset, error);
306
235k
    if (st_res == NULL)
307
212
      return FALSE;
308
235k
    address = fu_struct_fdt_reserve_entry_get_address(st_res);
309
235k
    size = fu_struct_fdt_reserve_entry_get_size(st_res);
310
235k
    g_debug("mem_rsvmap: 0x%x, 0x%x", (guint)address, (guint)size);
311
235k
    if (address == 0x0 && size == 0x0)
312
47
      break;
313
235k
  }
314
315
  /* success */
316
2.78k
  return TRUE;
317
2.99k
}
318
319
static gboolean
320
fu_fdt_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error)
321
3.58k
{
322
3.58k
  return fu_struct_fdt_validate_stream(stream, offset, error);
323
3.58k
}
324
325
static gboolean
326
fu_fdt_firmware_parse(FuFirmware *firmware,
327
          GInputStream *stream,
328
          FuFirmwareParseFlags flags,
329
          GError **error)
330
3.45k
{
331
3.45k
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
332
3.45k
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
333
3.45k
  guint32 totalsize;
334
3.45k
  gsize streamsz = 0;
335
3.45k
  guint32 off_mem_rsvmap = 0;
336
3.45k
  g_autoptr(FuStructFdt) st_hdr = NULL;
337
338
  /* sanity check */
339
3.45k
  st_hdr = fu_struct_fdt_parse_stream(stream, 0x0, error);
340
3.45k
  if (st_hdr == NULL)
341
0
    return FALSE;
342
3.45k
  if (!fu_input_stream_size(stream, &streamsz, error))
343
0
    return FALSE;
344
3.45k
  totalsize = fu_struct_fdt_get_totalsize(st_hdr);
345
3.45k
  if (totalsize > streamsz) {
346
45
    g_set_error(error,
347
45
          FWUPD_ERROR,
348
45
          FWUPD_ERROR_INVALID_DATA,
349
45
          "truncated image, got 0x%x, expected >= 0x%x",
350
45
          (guint)streamsz,
351
45
          (guint)totalsize);
352
45
    return FALSE;
353
45
  }
354
3.40k
  fu_firmware_set_size(firmware, totalsize);
355
356
  /* read header */
357
3.40k
  priv->cpuid = fu_struct_fdt_get_boot_cpuid_phys(st_hdr);
358
3.40k
  off_mem_rsvmap = fu_struct_fdt_get_off_mem_rsvmap(st_hdr);
359
3.40k
  if (off_mem_rsvmap != 0x0) {
360
2.99k
    if (!fu_fdt_firmware_parse_mem_rsvmap(self, stream, off_mem_rsvmap, error))
361
212
      return FALSE;
362
2.99k
  }
363
3.19k
  if (fu_struct_fdt_get_last_comp_version(st_hdr) < FDT_LAST_COMP_VERSION) {
364
26
    g_set_error(error,
365
26
          FWUPD_ERROR,
366
26
          FWUPD_ERROR_INVALID_DATA,
367
26
          "invalid header version, got 0x%x, expected >= 0x%x",
368
26
          (guint)fu_struct_fdt_get_last_comp_version(st_hdr),
369
26
          (guint)FDT_LAST_COMP_VERSION);
370
26
    return FALSE;
371
26
  }
372
3.16k
  fu_firmware_set_version_raw(firmware, fu_struct_fdt_get_version(st_hdr));
373
374
  /* parse device tree struct */
375
3.16k
  if (fu_struct_fdt_get_size_dt_struct(st_hdr) != 0x0 &&
376
3.16k
      fu_struct_fdt_get_size_dt_strings(st_hdr) != 0x0) {
377
3.15k
    g_autoptr(GByteArray) dt_strings = NULL;
378
3.15k
    g_autoptr(GByteArray) dt_struct = NULL;
379
3.15k
    g_autoptr(GBytes) dt_struct_buf = NULL;
380
3.15k
    dt_strings =
381
3.15k
        fu_input_stream_read_byte_array(stream,
382
3.15k
                fu_struct_fdt_get_off_dt_strings(st_hdr),
383
3.15k
                fu_struct_fdt_get_size_dt_strings(st_hdr),
384
3.15k
                NULL,
385
3.15k
                error);
386
3.15k
    if (dt_strings == NULL)
387
134
      return FALSE;
388
3.01k
    dt_struct =
389
3.01k
        fu_input_stream_read_byte_array(stream,
390
3.01k
                fu_struct_fdt_get_off_dt_struct(st_hdr),
391
3.01k
                fu_struct_fdt_get_size_dt_struct(st_hdr),
392
3.01k
                NULL,
393
3.01k
                error);
394
3.01k
    if (dt_struct == NULL)
395
50
      return FALSE;
396
2.96k
    if (dt_struct->len != fu_struct_fdt_get_size_dt_struct(st_hdr)) {
397
148
      g_set_error_literal(error,
398
148
              FWUPD_ERROR,
399
148
              FWUPD_ERROR_INVALID_DATA,
400
148
              "invalid firmware -- dt_struct invalid");
401
148
      return FALSE;
402
148
    }
403
2.82k
    dt_struct_buf =
404
2.82k
        g_byte_array_free_to_bytes(g_steal_pointer(&dt_struct)); /* nocheck:blocked */
405
2.82k
    if (!fu_fdt_firmware_parse_dt_struct(self, dt_struct_buf, dt_strings, error))
406
958
      return FALSE;
407
2.82k
  }
408
409
  /* success */
410
1.87k
  return TRUE;
411
3.16k
}
412
413
typedef struct {
414
  GByteArray *dt_strings;
415
  GByteArray *dt_struct;
416
  GHashTable *strtab;
417
} FuFdtFirmwareBuildHelper;
418
419
static guint32
420
fu_fdt_firmware_append_to_strtab(FuFdtFirmwareBuildHelper *helper, const gchar *key)
421
8.25k
{
422
8.25k
  gpointer tmp = NULL;
423
8.25k
  guint32 offset;
424
425
  /* already exists */
426
8.25k
  if (g_hash_table_lookup_extended(helper->strtab, key, NULL, &tmp))
427
1.78k
    return GPOINTER_TO_UINT(tmp);
428
429
6.46k
  g_debug("adding strtab: %s", key);
430
6.46k
  offset = helper->dt_strings->len;
431
6.46k
  g_byte_array_append(helper->dt_strings, (const guint8 *)key, strlen(key));
432
6.46k
  fu_byte_array_append_uint8(helper->dt_strings, 0x0);
433
6.46k
  g_hash_table_insert(helper->strtab, g_strdup(key), GUINT_TO_POINTER(offset));
434
6.46k
  return offset;
435
8.25k
}
436
437
static gboolean
438
fu_fdt_firmware_write_image(FuFdtFirmware *self,
439
          FuFdtImage *img,
440
          FuFdtFirmwareBuildHelper *helper,
441
          guint depth,
442
          GError **error)
443
6.88k
{
444
6.88k
  const gchar *id = fu_firmware_get_id(FU_FIRMWARE(img));
445
6.88k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(FU_FIRMWARE(img));
446
6.88k
  g_autoptr(GPtrArray) attrs = fu_fdt_image_get_attrs(img);
447
448
  /* sanity check */
449
6.88k
  if (depth > 0 && id == NULL) {
450
92
    g_set_error_literal(error,
451
92
            FWUPD_ERROR,
452
92
            FWUPD_ERROR_INVALID_DATA,
453
92
            "child FuFdtImage requires ID");
454
92
    return FALSE;
455
92
  }
456
457
  /* BEGIN_NODE, ID, NUL */
458
6.79k
  fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_BEGIN_NODE, G_BIG_ENDIAN);
459
6.79k
  if (id != NULL) {
460
5.78k
    g_byte_array_append(helper->dt_struct, (const guint8 *)id, strlen(id) + 1);
461
5.78k
  } else {
462
1.01k
    fu_byte_array_append_uint8(helper->dt_struct, 0x0);
463
1.01k
  }
464
6.79k
  fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0);
465
466
  /* write properties */
467
15.0k
  for (guint i = 0; i < attrs->len; i++) {
468
8.25k
    const gchar *key = g_ptr_array_index(attrs, i);
469
8.25k
    g_autoptr(GBytes) blob = NULL;
470
8.25k
    g_autoptr(FuStructFdtProp) st_prp = fu_struct_fdt_prop_new();
471
472
8.25k
    blob = fu_fdt_image_get_attr(img, key, error);
473
8.25k
    if (blob == NULL)
474
0
      return FALSE;
475
8.25k
    fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_PROP, G_BIG_ENDIAN);
476
8.25k
    fu_struct_fdt_prop_set_len(st_prp, g_bytes_get_size(blob));
477
8.25k
    fu_struct_fdt_prop_set_nameoff(st_prp,
478
8.25k
                 fu_fdt_firmware_append_to_strtab(helper, key));
479
8.25k
    fu_byte_array_append_array(helper->dt_struct, st_prp->buf);
480
8.25k
    fu_byte_array_append_bytes(helper->dt_struct, blob);
481
8.25k
    fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0);
482
8.25k
  }
483
484
  /* write children, recursively */
485
11.8k
  for (guint i = 0; i < images->len; i++) {
486
5.65k
    FuFdtImage *img_child = g_ptr_array_index(images, i);
487
5.65k
    if (!fu_fdt_firmware_write_image(self, img_child, helper, depth + 1, error))
488
587
      return FALSE;
489
5.65k
  }
490
491
  /* END_NODE */
492
6.20k
  fu_byte_array_append_uint32(helper->dt_struct, FU_FDT_TOKEN_END_NODE, G_BIG_ENDIAN);
493
6.20k
  return TRUE;
494
6.79k
}
495
496
static GByteArray *
497
fu_fdt_firmware_write(FuFirmware *firmware, GError **error)
498
1.25k
{
499
1.25k
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
500
1.25k
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
501
1.25k
  guint32 off_dt_struct;
502
1.25k
  guint32 off_dt_strings;
503
1.25k
  guint32 off_mem_rsvmap;
504
1.25k
  g_autoptr(GByteArray) dt_strings = g_byte_array_new();
505
1.25k
  g_autoptr(GByteArray) dt_struct = g_byte_array_new();
506
1.25k
  g_autoptr(GHashTable) strtab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
507
1.25k
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
508
1.25k
  g_autoptr(FuStructFdt) st_hdr = fu_struct_fdt_new();
509
1.25k
  g_autoptr(FuStructFdtReserveEntry) st_rsvmap = fu_struct_fdt_reserve_entry_new();
510
1.25k
  FuFdtFirmwareBuildHelper helper = {
511
1.25k
      .dt_strings = dt_strings,
512
1.25k
      .dt_struct = dt_struct,
513
1.25k
      .strtab = strtab,
514
1.25k
  };
515
516
  /* empty st_rsvmap */
517
1.25k
  off_mem_rsvmap = fu_common_align_up(st_hdr->buf->len, FU_FIRMWARE_ALIGNMENT_4);
518
519
  /* dt_struct */
520
1.25k
  off_dt_struct =
521
1.25k
      fu_common_align_up(off_mem_rsvmap + st_rsvmap->buf->len, FU_FIRMWARE_ALIGNMENT_4);
522
523
  /* only one root node supported */
524
1.25k
  if (images->len != 1) {
525
24
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "no root node");
526
24
    return NULL;
527
24
  }
528
1.23k
  if (!fu_fdt_firmware_write_image(self,
529
1.23k
           FU_FDT_IMAGE(g_ptr_array_index(images, 0)),
530
1.23k
           &helper,
531
1.23k
           0,
532
1.23k
           error))
533
92
    return NULL;
534
1.14k
  fu_byte_array_append_uint32(dt_struct, FU_FDT_TOKEN_END, G_BIG_ENDIAN);
535
536
  /* dt_strings */
537
1.14k
  off_dt_strings =
538
1.14k
      fu_common_align_up(off_dt_struct + dt_struct->len, FU_FIRMWARE_ALIGNMENT_4);
539
540
  /* write header */
541
1.14k
  fu_struct_fdt_set_totalsize(st_hdr, off_dt_strings + dt_strings->len);
542
1.14k
  fu_struct_fdt_set_off_dt_struct(st_hdr, off_dt_struct);
543
1.14k
  fu_struct_fdt_set_off_dt_strings(st_hdr, off_dt_strings);
544
1.14k
  fu_struct_fdt_set_off_mem_rsvmap(st_hdr, off_mem_rsvmap);
545
1.14k
  fu_struct_fdt_set_version(st_hdr, fu_firmware_get_version_raw(firmware));
546
1.14k
  fu_struct_fdt_set_boot_cpuid_phys(st_hdr, priv->cpuid);
547
1.14k
  fu_struct_fdt_set_size_dt_strings(st_hdr, dt_strings->len);
548
1.14k
  fu_struct_fdt_set_size_dt_struct(st_hdr, dt_struct->len);
549
1.14k
  fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0);
550
551
  /* write st_rsvmap, dt_struct, dt_strings */
552
1.14k
  fu_byte_array_append_array(st_hdr->buf, st_rsvmap->buf);
553
1.14k
  fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0);
554
1.14k
  fu_byte_array_append_array(st_hdr->buf, dt_struct);
555
1.14k
  fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0);
556
1.14k
  fu_byte_array_append_array(st_hdr->buf, dt_strings);
557
1.14k
  fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0);
558
559
  /* success */
560
1.14k
  return g_steal_pointer(&st_hdr->buf);
561
1.23k
}
562
563
static gboolean
564
fu_fdt_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
565
0
{
566
0
  FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware);
567
0
  FuFdtFirmwarePrivate *priv = GET_PRIVATE(self);
568
0
  guint64 tmp;
569
570
  /* optional properties */
571
0
  tmp = xb_node_query_text_as_uint(n, "cpuid", NULL);
572
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
573
0
    priv->cpuid = tmp;
574
575
  /* success */
576
0
  return TRUE;
577
0
}
578
579
static void
580
fu_fdt_firmware_init(FuFdtFirmware *self)
581
3.58k
{
582
3.58k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FDT_IMAGE);
583
3.58k
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_VID_PID);
584
3.58k
}
585
586
static void
587
fu_fdt_firmware_class_init(FuFdtFirmwareClass *klass)
588
2
{
589
2
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
590
2
  firmware_class->validate = fu_fdt_firmware_validate;
591
2
  firmware_class->export = fu_fdt_firmware_export;
592
2
  firmware_class->parse = fu_fdt_firmware_parse;
593
2
  firmware_class->write = fu_fdt_firmware_write;
594
2
  firmware_class->build = fu_fdt_firmware_build;
595
2
}
596
597
/**
598
 * fu_fdt_firmware_new:
599
 *
600
 * Creates a new #FuFirmware of sub type FDT
601
 *
602
 * Since: 1.8.2
603
 **/
604
FuFirmware *
605
fu_fdt_firmware_new(void)
606
0
{
607
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_FDT_FIRMWARE, NULL));
608
0
}