Coverage Report

Created: 2026-10-17 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/common/oftree.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#include <common.h>
4
#include <environment.h>
5
#include <fdt.h>
6
#include <of.h>
7
#include <command.h>
8
#include <fs.h>
9
#include <malloc.h>
10
#include <linux/ctype.h>
11
#include <asm/byteorder.h>
12
#include <errno.h>
13
#include <getopt.h>
14
#include <init.h>
15
#include <boot.h>
16
#include <bootsource.h>
17
#include <i2c/i2c.h>
18
#include <reset_source.h>
19
#include <watchdog.h>
20
#include <globalvar.h>
21
#include <magicvar.h>
22
23
#define MAX_LEVEL 32    /* how deeply nested we will go */
24
25
static int is_printable_string(const void *data, int len)
26
0
{
27
0
  const char *s = data;
28
29
  /* zero length is not */
30
0
  if (len == 0)
31
0
    return 0;
32
33
  /* must terminate with zero */
34
0
  if (s[len - 1] != '\0')
35
0
    return 0;
36
37
  /* printable or a null byte (concatenated strings) */
38
0
  while (((*s == '\0') || isprint(*s)) && (len > 0)) {
39
    /*
40
     * If we see a null, there are three possibilities:
41
     * 1) If len == 1, it is the end of the string, printable
42
     * 2) Next character also a null, not printable.
43
     * 3) Next character not a null, continue to check.
44
     */
45
0
    if (s[0] == '\0') {
46
0
      if (len == 1)
47
0
        return 1;
48
0
      if (s[1] == '\0')
49
0
        return 0;
50
0
    }
51
0
    s++;
52
0
    len--;
53
0
  }
54
55
  /* Not the null termination, or not done yet: not printable */
56
0
  if (*s != '\0' || (len != 0))
57
0
    return 0;
58
59
0
  return 1;
60
0
}
61
62
/*
63
 * Print the property in the best format, a heuristic guess.  Print as
64
 * a string, concatenated strings, a byte, word, double word, or (if all
65
 * else fails) it is printed as a stream of bytes.
66
 */
67
void of_print_property(const void *data, int len)
68
0
{
69
0
  int j;
70
71
  /* no data, don't print */
72
0
  if (len == 0)
73
0
    return;
74
75
  /*
76
   * It is a string, but it may have multiple strings (embedded '\0's).
77
   */
78
0
  if (is_printable_string(data, len)) {
79
0
    puts("\"");
80
0
    j = 0;
81
0
    while (j < len) {
82
0
      if (j > 0)
83
0
        puts("\", \"");
84
0
      puts(data);
85
0
      j    += strlen(data) + 1;
86
0
      data += strlen(data) + 1;
87
0
    }
88
0
    puts("\"");
89
0
    return;
90
0
  }
91
92
0
  if ((len % 4) == 0) {
93
0
    const u32 *p;
94
95
0
    printf("<");
96
0
    for (j = 0, p = data; j < len/4; j ++)
97
0
      printf("0x%x%s", fdt32_to_cpu(p[j]), j < (len/4 - 1) ? " " : "");
98
0
    printf(">");
99
0
  } else { /* anything else... hexdump */
100
0
    const u8 *s;
101
102
0
    printf("[");
103
0
    for (j = 0, s = data; j < len; j++)
104
0
      printf("%02x%s", s[j], j < len - 1 ? " " : "");
105
0
    printf("]");
106
0
  }
107
0
}
108
109
void of_print_cmdline(struct device_node *root)
110
0
{
111
0
  struct device_node *node = of_find_node_by_path_from(root, "/chosen");
112
0
  const char *cmdline;
113
114
0
  if (!node) {
115
0
    printf("commandline: no /chosen node\n");
116
0
    return;
117
0
  }
118
119
0
  cmdline = of_get_property(node, "bootargs", NULL);
120
121
0
  pr_info("commandline: %s\n", cmdline);
122
0
}
123
124
static int of_fixup_bootargs_bootsource(struct device_node *root,
125
          struct device_node *chosen)
126
0
{
127
0
  struct device_node *bootsource;
128
0
  int ret = 0;
129
130
0
  bootsource = bootsource_of_node_get(root);
131
  /*
132
   * If kernel DTB doesn't have the appropriate alias set up,
133
   * give up and exit early. No error is reported.
134
   */
135
0
  if (bootsource)
136
0
    ret = of_set_property(chosen, "bootsource", bootsource->full_name,
137
0
              strlen(bootsource->full_name) + 1, true);
138
139
0
  return ret;
140
0
}
141
142
static int of_fixup_bootargs_reset_source(struct device_node *root,
143
            struct device_node *chosen)
144
0
{
145
0
  struct device_node *np;
146
0
  struct device *dev;
147
0
  char *name;
148
149
0
  dev = reset_source_get_device();
150
0
  if (!dev || !dev->of_node)
151
0
    return 0;
152
153
0
  name = of_get_reproducible_name(dev->of_node);
154
0
  np = of_find_node_by_reproducible_name(root, name);
155
0
  free(name);
156
157
0
  if (!np)
158
0
    return 0;
159
160
0
  return of_property_write_string(chosen, "reset-source-device", np->full_name);
161
0
}
162
163
static void watchdog_build_bootargs(struct watchdog *watchdog, struct device_node *root)
164
0
{
165
0
  int alias_id;
166
0
  char *buf;
167
0
168
0
  if (!watchdog)
169
0
    return;
170
0
171
0
  alias_id = watchdog_get_alias_id_from(watchdog, root);
172
0
  if (alias_id < 0)
173
0
    return;
174
0
175
0
  buf = xasprintf("systemd.watchdog_device=/dev/watchdog%d", alias_id);
176
0
177
0
  globalvar_add_simple("linux.bootargs.dyn.watchdog", buf);
178
0
  free(buf);
179
0
}
180
181
static int bootargs_append = 0;
182
BAREBOX_MAGICVAR(global.linux.bootargs_append, "append to original oftree bootargs");
183
184
static int of_write_bootargs(struct device_node *node)
185
0
{
186
0
  const char *str;
187
0
  char *buf = NULL;
188
0
  int ret;
189
190
0
  if (IS_ENABLED(CONFIG_SYSTEMD_OF_WATCHDOG))
191
0
    watchdog_build_bootargs(boot_get_enabled_watchdog(), of_get_parent(node));
192
193
0
  str = linux_bootargs_get();
194
0
  if (!str)
195
0
    return 0;
196
197
0
  str = skip_spaces(str);
198
0
  if (strlen(str) == 0)
199
0
    return 0;
200
201
0
  if (bootargs_append) {
202
0
    const char *oldstr;
203
204
0
    ret = of_property_read_string(node, "bootargs", &oldstr);
205
0
    if (!ret)
206
0
      str = buf = xasprintf("%s %s", oldstr, str);
207
0
  }
208
209
0
  ret = of_property_write_string(node, "bootargs", str);
210
0
  free(buf);
211
0
  return ret;
212
0
}
213
214
static int of_fixup_bootargs(struct device_node *root, void *unused)
215
0
{
216
0
  struct device_node *node;
217
0
  int err;
218
0
  int instance = reset_source_get_instance();
219
0
  const char *serialno;
220
0
  const char *compat;
221
222
0
  serialno = barebox_get_serial_number();
223
0
  if (serialno)
224
0
    of_property_write_string(root, "serial-number", serialno);
225
226
0
  compat = barebox_get_of_machine_compatible();
227
0
  if (compat)
228
0
    of_prepend_machine_compatible(root, compat);
229
230
0
  node = of_create_node(root, "/chosen");
231
0
  if (!node)
232
0
    return -ENOMEM;
233
234
0
  of_property_write_string(node, "barebox-version", release_string);
235
236
0
  err = of_write_bootargs(node);
237
0
  if (err)
238
0
    return err;
239
240
0
  of_property_write_string(node, "reset-source", reset_source_name());
241
0
  if (instance >= 0)
242
0
    of_property_write_u32(node, "reset-source-instance", instance);
243
244
245
0
  err = of_fixup_bootargs_reset_source(root, node);
246
0
  if (err)
247
0
    return err;
248
249
0
  err = of_fixup_bootargs_bootsource(root, node);
250
0
  if (err)
251
0
    return err;
252
253
0
  if (IS_ENABLED(CONFIG_RISCV)) {
254
0
    const char *hartid;
255
256
0
    hartid = getenv("global.hartid");
257
0
    if (hartid) {
258
0
      unsigned long id;
259
260
0
      err = kstrtoul(hartid, 10, &id);
261
0
      if (!err)
262
0
        err = of_property_write_u32(node, "boot-hartid", id);
263
0
    }
264
0
  }
265
266
0
  return err;
267
0
}
268
269
static int of_register_bootargs_fixup(void)
270
26
{
271
26
  globalvar_add_simple_bool("linux.bootargs_append", &bootargs_append);
272
26
  return of_register_fixup(of_fixup_bootargs, NULL);
273
26
}
274
late_initcall(of_register_bootargs_fixup);
275
276
int of_fixup_reserved_memory(struct device_node *root, void *_res)
277
0
{
278
0
  struct resource *res = _res;
279
0
  struct device_node *node, *child;
280
0
  struct property *pp;
281
0
  unsigned addr_n_cells = sizeof(void *) / sizeof(__be32),
282
0
     size_n_cells = sizeof(void *) / sizeof(__be32);
283
0
  unsigned rangelen = 0;
284
0
  __be32 reg[4];
285
0
  int ret;
286
287
0
  node = of_get_child_by_name(root, "reserved-memory") ?: of_new_node(root, "reserved-memory");
288
289
0
  ret = of_property_read_u32(node, "#address-cells", &addr_n_cells);
290
0
  if (ret)
291
0
    of_property_write_u32(node, "#address-cells", addr_n_cells);
292
293
0
  ret = of_property_read_u32(node, "#size-cells", &size_n_cells);
294
0
  if (ret)
295
0
    of_property_write_u32(node, "#size-cells", size_n_cells);
296
297
0
  pp = of_find_property(node, "ranges", &rangelen);
298
0
  if (!pp) {
299
0
    of_new_property(node, "ranges", NULL, 0);
300
0
  } else if (rangelen) {
301
0
    pr_warn("reserved-memory ranges not 1:1 mapped. Aborting fixup\n");
302
0
    return -EINVAL;
303
0
  }
304
305
0
  child = of_get_child_by_name(node, res->name) ?: of_new_node(node, res->name);
306
307
0
  if (is_reserved_resource(res))
308
0
    of_property_write_bool(child, "no-map", true);
309
310
0
  of_write_number(reg, res->start, addr_n_cells);
311
0
  of_write_number(reg + addr_n_cells, resource_size(res), size_n_cells);
312
313
0
  of_set_property(child, "reg", reg,
314
0
      (addr_n_cells + size_n_cells) * sizeof(*reg), true);
315
316
0
  return 0;
317
0
}
318
319
struct of_fixup_status_data {
320
  const char *path;
321
  bool status;
322
};
323
324
static int of_fixup_status(struct device_node *root, void *context)
325
0
{
326
0
  const struct of_fixup_status_data *data = context;
327
0
  struct device_node *node;
328
329
0
  node = of_find_node_by_path_or_alias(root, data->path);
330
0
  if (!node)
331
0
    return -ENODEV;
332
333
0
  if (data->status)
334
0
    return of_device_enable(node);
335
0
  else
336
0
    return of_device_disable(node);
337
0
}
338
339
/**
340
 * of_register_set_status_fixup - register fix up to set status of nodes
341
 * Register a fixup to enable or disable a node in the devicet tree by
342
 * passing the path or alias.
343
 */
344
int of_register_set_status_fixup(const char *path, bool status)
345
0
{
346
0
  struct of_fixup_status_data *data;
347
348
0
  data = xzalloc(sizeof(*data));
349
0
  data->path = path;
350
0
  data->status = status;
351
352
0
  return of_register_fixup(of_fixup_status, (void *)data);
353
0
}
354
355
LIST_HEAD(of_fixup_list);
356
357
static inline bool of_fixup_disabled(struct of_fixup *fixup)
358
26
{
359
26
    return fixup->disabled;
360
26
}
361
362
int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context)
363
559
{
364
559
  struct of_fixup *of_fixup = xzalloc(sizeof(*of_fixup));
365
366
559
  of_fixup->fixup = fixup;
367
559
  of_fixup->context = context;
368
369
559
  list_add_tail(&of_fixup->list, &of_fixup_list);
370
371
559
  return 0;
372
559
}
373
374
/*
375
 * Remove a previously registered fixup. Only the first (if any) is removed.
376
 * Returns 0 if a match was found (and removed), -ENOENT otherwise.
377
 */
378
int of_unregister_fixup(int (*fixup)(struct device_node *, void *),
379
      void *context)
380
674
{
381
674
  struct of_fixup *of_fixup;
382
383
5.09k
  list_for_each_entry(of_fixup, &of_fixup_list, list) {
384
5.09k
    if (of_fixup->fixup == fixup && of_fixup->context == context) {
385
377
      list_del(&of_fixup->list);
386
377
      free(of_fixup);
387
377
      return 0;
388
377
    }
389
5.09k
  }
390
391
297
  return -ENOENT;
392
674
}
393
394
/*
395
 * Apply registered fixups for the given fdt. The fdt must have
396
 * enough free space to apply the fixups.
397
 */
398
void of_fix_tree(struct device_node *node)
399
26
{
400
26
  struct of_fixup *of_fixup;
401
26
  int ret;
402
403
26
  of_overlay_load_firmware_clear();
404
405
26
  list_for_each_entry(of_fixup, &of_fixup_list, list) {
406
26
    if (of_fixup_disabled(of_fixup))
407
0
      continue;
408
409
26
    ret = of_fixup->fixup(node, of_fixup->context);
410
26
    if (ret)
411
26
      pr_warn("Failed to fixup node in %pS: %pe\n",
412
26
          of_fixup->fixup, ERR_PTR(ret));
413
26
  }
414
26
}
415
416
/*
417
 * Get the fdt. This function uses the fdt input pointer
418
 * if provided or the barebox internal devicetree if not.
419
 */
420
struct fdt_header *of_get_flattened_tree(const struct device_node *node,
421
           bool fixup)
422
0
{
423
0
  struct fdt_header *fdt = NULL;
424
0
  struct device_node *np = NULL;
425
426
0
  if (!node) {
427
0
    node = of_get_root_node();
428
0
    if (!node)
429
0
      return NULL;
430
0
  }
431
432
0
  if (fixup)
433
0
    node = np = of_dup(node);
434
435
0
  if (!node)
436
0
    return NULL;
437
438
0
  if (fixup)
439
0
    of_fix_tree(np);
440
441
0
  fdt = of_flatten_dtb((struct device_node *)node);
442
443
0
  if (fixup)
444
0
    of_delete_node(np);
445
446
0
  return fdt;
447
0
}
448
449
/**
450
 * of_autoenable_device_by_path() - Autoenable a device by a device tree path
451
 * @param path Device tree path up from the root to the device
452
 * @return 0 on success, -enodev on failure. If no device found in the device
453
 * tree.
454
 *
455
 * This function will search for a device and will enable it in the kernel
456
 * device tree, if it exists and is loaded.
457
 */
458
int of_autoenable_device_by_path(char *path)
459
0
{
460
0
  struct device_node *node;
461
0
  int ret;
462
463
0
  node = of_find_node_by_name_address(NULL, path);
464
0
  if (!node)
465
0
    node = of_find_node_by_path(path);
466
467
0
  if (!node)
468
0
    return -ENODEV;
469
470
0
  if (!of_device_is_available(node))
471
0
      return -ENODEV;
472
473
0
  ret = of_register_set_status_fixup(path, 1);
474
0
  if (!ret)
475
0
    printf("autoenabled %s\n", node->name);
476
0
  return ret;
477
0
}
478
479
/**
480
 * of_autoenable_i2c_by_component - Autoenable a i2c client by a device tree path
481
 * @param path Device tree path up from the root to the i2c client
482
 * @return 0 on success, -enodev on failure. If no i2c client found in the i2c
483
 * device tree.
484
 *
485
 * This function will search for a i2c client, tries to write to the client and
486
 * will enable it in the kernel device tree, if it exists and is accessible.
487
 */
488
int of_autoenable_i2c_by_component(char *path)
489
0
{
490
0
  struct device_node *node;
491
0
  struct i2c_adapter *i2c_adapter;
492
0
  struct i2c_msg msg;
493
0
  char data[1] = {0x0};
494
0
  int ret;
495
0
  uint32_t addr;
496
497
0
  if (!IS_ENABLED(CONFIG_I2C))
498
0
    return -ENODEV;
499
500
0
  node = of_find_node_by_name_address(NULL, path);
501
0
  if (!node)
502
0
    node = of_find_node_by_path(path);
503
0
  if (!node)
504
0
    return -ENODEV;
505
0
  if (!node->parent)
506
0
    return -ENODEV;
507
508
0
  ret = of_property_read_u32(node, "reg", &addr);
509
0
  if (ret)
510
0
    return -ENODEV;
511
512
0
  i2c_adapter = of_find_i2c_adapter_by_node(node->parent);
513
0
  if (!i2c_adapter)
514
0
    return -ENODEV;
515
516
0
  msg.buf = data;
517
0
  msg.addr = addr;
518
0
  msg.len = 1;
519
520
  /* Try to communicate with the i2c client */
521
0
  ret = i2c_transfer(i2c_adapter, &msg, 1);
522
0
  if (ret == -EREMOTEIO)
523
0
    return -ENODEV;
524
0
  if (ret < 1) {
525
0
    printf("failed to autoenable i2c device on address 0x%x with %i\n",
526
0
                addr, ret);
527
0
    return ret;
528
0
  }
529
530
0
  ret = of_register_set_status_fixup(path, 1);
531
0
  if (!ret)
532
0
    printf("autoenabled i2c device %s\n", node->name);
533
534
0
  return ret;
535
0
}
536
537
int of_prepend_machine_compatible(struct device_node *root, const char *compat)
538
0
{
539
0
  int cclen = 0, clen = strlen(compat) + 1;
540
0
  const char *curcompat;
541
0
  void *buf;
542
543
0
  if (!root) {
544
0
    root = of_get_root_node();
545
0
    if (!root)
546
0
      return -ENODEV;
547
0
  }
548
549
0
  if (of_device_is_compatible(root, compat))
550
0
    return 0;
551
552
0
  curcompat = of_get_property(root, "compatible", &cclen);
553
0
  if (!curcompat)
554
0
    return -ENOENT;
555
556
0
  buf = xzalloc(cclen + clen);
557
558
0
  memcpy(buf, compat, clen);
559
560
0
  if (curcompat)
561
0
    memcpy(buf + clen, curcompat, cclen);
562
563
0
  of_set_property(root, "compatible", buf, cclen + clen, true);
564
565
0
  free(buf);
566
567
0
  return 0;
568
0
}