mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-04 12:21:03 +02:00
Synchronize local copy of DTC with Linux 6.17 . This includes the
following picked and squashed commits from Linux kernel. The squash
was necessary, since the DTC here contains changes which were also
part of DTC in Linux alraedy, and the squash helped resolve those
without going back and forth with the changes.
The following commits from Linux are picked:
8f324cd712df7 # scripts/dtc: consolidate include path options in Makefile
b5b3d9b63b0ee # scripts/dtc: Add yamltree.c to dtc sources
7d97a76f226d6 # scripts/dtc: Update to upstream version v1.4.7-14-gc86da84d30e4
ea6f243be74e5 # scripts/dtc: Update to upstream version v1.4.7-57-gf267e674d145
02d435d4eccd8 # scripts/dtc: Update to upstream version v1.5.0-23-g87963ee20693
6e321b7637396 # scripts/dtc: Update to upstream version v1.5.0-30-g702c1b6c0e73
9f19ec91a7a35 # scripts/dtc: dtx_diff - add color output support
8287d642f38d1 # scripts/dtc: Update to upstream version v1.5.1-22-gc40aeb60b47a
4c52deef9225d # scripts/dtc: Revert "yamltree: Ensure consistent bracketing of properties with phandles"
5d3827e1452ed # scripts/dtc: Remove unused makefile fragments
40dd266887654 # scripts/dtc: Update to upstream version v1.6.0-2-g87a656ae5ff9
8d4cf6b6acb59 # scripts/dtc: use pkg-config to include <yaml.h> in non-standard path
b9bf9ace5ae90 # scripts/dtc: Update to upstream version v1.6.0-11-g9d7888cbf19c
69a883b6f5ac0 # scripts/dtc: dtx_diff - make help text formatting consistent
8f829108b8aed # scripts/dtc: only append to HOST_EXTRACFLAGS instead of overwriting
b39b4342ac495 # scripts/dtc: Update to upstream version v1.6.0-31-gcbca977ea121
93c6424c486b3 # scripts: dtc: Fetch fdtoverlay.c from external DTC project
0dd574a1d75c3 # scripts/dtc: Update to upstream version v1.6.0-51-g183df9e9c2b9
ec38b5df8a231 # scripts: dtc: Build fdtoverlay tool
a0c8c431411f5 # scripts: dtc: Remove the unused fdtdump.c file
e7dc653d4e890 # scripts/dtc: Add missing fdtoverlay to gitignore
d2bf5d2e3f09c # scripts/dtc: Update to upstream version v1.6.1-19-g0a3a9d3449c8
a60878f5532d0 # scripts/dtc: dtx_diff: remove broken example from help text
8b739d8658a9b # scripts/dtc: Call pkg-config POSIXly correct
b6eeafa67df00 # scripts/dtc: Update to upstream version v1.6.1-63-g55778a03df61
f96cc4c787588 # scripts/dtc: Update to upstream version v1.6.1-66-gabbd523bae6e
09ab9c092ef2b # scripts/dtc: Update to upstream version v1.7.0-93-g1df7b047fe43
ded8a5a498f2d # scripts/dtc: Update to upstream version v1.7.0-95-gbcd02b523429
ee6ff6fca7e71 # scripts/dtc: Update to upstream version v1.7.2-35-g52f07dcca47c
This also includes forward port of U-Boot commit
e8c2d25845c7 ("libfdt: Revert 6dcb8ba4 from upstream libfdt")
to avoid binary size growth.
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
508 lines
12 KiB
C
508 lines
12 KiB
C
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
|
|
/*
|
|
* libfdt - Flat Device Tree manipulation
|
|
* Copyright (C) 2006 David Gibson, IBM Corporation.
|
|
*/
|
|
#include "libfdt_env.h"
|
|
|
|
#include <fdt.h>
|
|
#include <libfdt.h>
|
|
|
|
#include "libfdt_internal.h"
|
|
|
|
static int fdt_blocks_misordered_(const void *fdt,
|
|
int mem_rsv_size, int struct_size)
|
|
{
|
|
return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
|
|
|| (fdt_off_dt_struct(fdt) <
|
|
(fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
|
|
|| (fdt_off_dt_strings(fdt) <
|
|
(fdt_off_dt_struct(fdt) + struct_size))
|
|
|| (fdt_totalsize(fdt) <
|
|
(fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
|
|
}
|
|
|
|
static int fdt_rw_probe_(void *fdt)
|
|
{
|
|
if (can_assume(VALID_DTB))
|
|
return 0;
|
|
FDT_RO_PROBE(fdt);
|
|
|
|
if (!can_assume(LATEST) && fdt_version(fdt) < 17)
|
|
return -FDT_ERR_BADVERSION;
|
|
if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
|
|
fdt_size_dt_struct(fdt)))
|
|
return -FDT_ERR_BADLAYOUT;
|
|
if (!can_assume(LATEST) && fdt_version(fdt) > 17)
|
|
fdt_set_version(fdt, 17);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define FDT_RW_PROBE(fdt) \
|
|
{ \
|
|
int err_; \
|
|
if ((err_ = fdt_rw_probe_(fdt)) != 0) \
|
|
return err_; \
|
|
}
|
|
|
|
static inline unsigned int fdt_data_size_(void *fdt)
|
|
{
|
|
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
|
}
|
|
|
|
static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
|
|
{
|
|
char *p = splicepoint;
|
|
unsigned int dsize = fdt_data_size_(fdt);
|
|
size_t soff = p - (char *)fdt;
|
|
|
|
if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
|
|
return -FDT_ERR_BADOFFSET;
|
|
if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
|
|
return -FDT_ERR_BADOFFSET;
|
|
if (dsize - oldlen + newlen > fdt_totalsize(fdt))
|
|
return -FDT_ERR_NOSPACE;
|
|
memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
|
|
return 0;
|
|
}
|
|
|
|
static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
|
|
int oldn, int newn)
|
|
{
|
|
int delta = (newn - oldn) * sizeof(*p);
|
|
int err;
|
|
err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
|
|
if (err)
|
|
return err;
|
|
fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
|
|
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
|
|
return 0;
|
|
}
|
|
|
|
static int fdt_splice_struct_(void *fdt, void *p,
|
|
int oldlen, int newlen)
|
|
{
|
|
int delta = newlen - oldlen;
|
|
int err;
|
|
|
|
if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
|
|
return err;
|
|
|
|
fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
|
|
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
|
|
return 0;
|
|
}
|
|
|
|
/* Must only be used to roll back in case of error */
|
|
static void fdt_del_last_string_(void *fdt, const char *s)
|
|
{
|
|
int newlen = strlen(s) + 1;
|
|
|
|
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
|
|
}
|
|
|
|
static int fdt_splice_string_(void *fdt, int newlen)
|
|
{
|
|
void *p = (char *)fdt
|
|
+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
|
|
int err;
|
|
|
|
if ((err = fdt_splice_(fdt, p, 0, newlen)))
|
|
return err;
|
|
|
|
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* fdt_find_add_string_() - Find or allocate a string
|
|
*
|
|
* @fdt: pointer to the device tree to check/adjust
|
|
* @s: string to find/add
|
|
* @allocated: Set to 0 if the string was found, 1 if not found and so
|
|
* allocated. Ignored if can_assume(NO_ROLLBACK)
|
|
* @return offset of string in the string table (whether found or added)
|
|
*/
|
|
static int fdt_find_add_string_(void *fdt, const char *s, int slen,
|
|
int *allocated)
|
|
{
|
|
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
|
|
const char *p;
|
|
char *new;
|
|
int err;
|
|
|
|
if (!can_assume(NO_ROLLBACK))
|
|
*allocated = 0;
|
|
|
|
p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen);
|
|
if (p)
|
|
/* found it */
|
|
return (p - strtab);
|
|
|
|
new = strtab + fdt_size_dt_strings(fdt);
|
|
err = fdt_splice_string_(fdt, slen + 1);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!can_assume(NO_ROLLBACK))
|
|
*allocated = 1;
|
|
|
|
memcpy(new, s, slen);
|
|
new[slen] = '\0';
|
|
|
|
return (new - strtab);
|
|
}
|
|
|
|
int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
|
|
{
|
|
struct fdt_reserve_entry *re;
|
|
int err;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
|
|
err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
|
|
if (err)
|
|
return err;
|
|
|
|
re->address = cpu_to_fdt64(address);
|
|
re->size = cpu_to_fdt64(size);
|
|
return 0;
|
|
}
|
|
|
|
int fdt_del_mem_rsv(void *fdt, int n)
|
|
{
|
|
struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
if (n >= fdt_num_mem_rsv(fdt))
|
|
return -FDT_ERR_NOTFOUND;
|
|
|
|
return fdt_splice_mem_rsv_(fdt, re, 1, 0);
|
|
}
|
|
|
|
static int fdt_resize_property_(void *fdt, int nodeoffset,
|
|
const char *name, int namelen,
|
|
int len, struct fdt_property **prop)
|
|
{
|
|
int oldlen;
|
|
int err;
|
|
|
|
*prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen,
|
|
&oldlen);
|
|
if (!*prop)
|
|
return oldlen;
|
|
|
|
if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
|
|
FDT_TAGALIGN(len))))
|
|
return err;
|
|
|
|
(*prop)->len = cpu_to_fdt32(len);
|
|
return 0;
|
|
}
|
|
|
|
static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
|
|
int namelen, int len, struct fdt_property **prop)
|
|
{
|
|
int proplen;
|
|
int nextoffset;
|
|
int namestroff;
|
|
int err;
|
|
int allocated;
|
|
|
|
if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
|
|
return nextoffset;
|
|
|
|
namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);
|
|
if (namestroff < 0)
|
|
return namestroff;
|
|
|
|
*prop = fdt_offset_ptr_w_(fdt, nextoffset);
|
|
proplen = sizeof(**prop) + FDT_TAGALIGN(len);
|
|
|
|
err = fdt_splice_struct_(fdt, *prop, 0, proplen);
|
|
if (err) {
|
|
/* Delete the string if we failed to add it */
|
|
if (!can_assume(NO_ROLLBACK) && allocated)
|
|
fdt_del_last_string_(fdt, name);
|
|
return err;
|
|
}
|
|
|
|
(*prop)->tag = cpu_to_fdt32(FDT_PROP);
|
|
(*prop)->nameoff = cpu_to_fdt32(namestroff);
|
|
(*prop)->len = cpu_to_fdt32(len);
|
|
return 0;
|
|
}
|
|
|
|
int fdt_set_name(void *fdt, int nodeoffset, const char *name)
|
|
{
|
|
char *namep;
|
|
int oldlen, newlen;
|
|
int err;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
|
|
if (!namep)
|
|
return oldlen;
|
|
|
|
newlen = strlen(name);
|
|
|
|
err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
|
|
FDT_TAGALIGN(newlen+1));
|
|
if (err)
|
|
return err;
|
|
|
|
memcpy(namep, name, newlen+1);
|
|
return 0;
|
|
}
|
|
|
|
int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
|
|
int namelen, int len, void **prop_data)
|
|
{
|
|
struct fdt_property *prop;
|
|
int err;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop);
|
|
if (err == -FDT_ERR_NOTFOUND)
|
|
err = fdt_add_property_(fdt, nodeoffset, name, namelen, len,
|
|
&prop);
|
|
if (err)
|
|
return err;
|
|
|
|
*prop_data = prop->data;
|
|
return 0;
|
|
}
|
|
|
|
int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
|
|
int namelen, const void *val, int len)
|
|
{
|
|
void *prop_data;
|
|
int err;
|
|
|
|
err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen,
|
|
len, &prop_data);
|
|
if (err)
|
|
return err;
|
|
|
|
if (len)
|
|
memcpy(prop_data, val, len);
|
|
return 0;
|
|
}
|
|
|
|
int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
|
|
const void *val, int len)
|
|
{
|
|
struct fdt_property *prop;
|
|
int err, oldlen, newlen;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
|
|
if (prop) {
|
|
newlen = len + oldlen;
|
|
err = fdt_splice_struct_(fdt, prop->data,
|
|
FDT_TAGALIGN(oldlen),
|
|
FDT_TAGALIGN(newlen));
|
|
if (err)
|
|
return err;
|
|
prop->len = cpu_to_fdt32(newlen);
|
|
memcpy(prop->data + oldlen, val, len);
|
|
} else {
|
|
err = fdt_add_property_(fdt, nodeoffset, name, strlen(name),
|
|
len, &prop);
|
|
if (err)
|
|
return err;
|
|
memcpy(prop->data, val, len);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int fdt_delprop(void *fdt, int nodeoffset, const char *name)
|
|
{
|
|
struct fdt_property *prop;
|
|
int len, proplen;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
|
|
if (!prop)
|
|
return len;
|
|
|
|
proplen = sizeof(*prop) + FDT_TAGALIGN(len);
|
|
return fdt_splice_struct_(fdt, prop, proplen, 0);
|
|
}
|
|
|
|
int fdt_add_subnode_namelen(void *fdt, int parentoffset,
|
|
const char *name, int namelen)
|
|
{
|
|
struct fdt_node_header *nh;
|
|
int offset, nextoffset;
|
|
int nodelen;
|
|
int err;
|
|
uint32_t tag;
|
|
fdt32_t *endtag;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
|
|
if (offset >= 0)
|
|
return -FDT_ERR_EXISTS;
|
|
else if (offset != -FDT_ERR_NOTFOUND)
|
|
return offset;
|
|
|
|
/* Try to place the new node after the parent's properties */
|
|
tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
|
|
/* the fdt_subnode_offset_namelen() should ensure this never hits */
|
|
if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
|
|
return -FDT_ERR_INTERNAL;
|
|
do {
|
|
offset = nextoffset;
|
|
tag = fdt_next_tag(fdt, offset, &nextoffset);
|
|
} while ((tag == FDT_PROP) || (tag == FDT_NOP));
|
|
|
|
nh = fdt_offset_ptr_w_(fdt, offset);
|
|
nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
|
|
|
|
err = fdt_splice_struct_(fdt, nh, 0, nodelen);
|
|
if (err)
|
|
return err;
|
|
|
|
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
|
|
memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
|
|
memcpy(nh->name, name, namelen);
|
|
endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
|
|
*endtag = cpu_to_fdt32(FDT_END_NODE);
|
|
|
|
return offset;
|
|
}
|
|
|
|
int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
|
|
{
|
|
return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
|
|
}
|
|
|
|
int fdt_del_node(void *fdt, int nodeoffset)
|
|
{
|
|
int endoffset;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
endoffset = fdt_node_end_offset_(fdt, nodeoffset);
|
|
if (endoffset < 0)
|
|
return endoffset;
|
|
|
|
return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
|
|
endoffset - nodeoffset, 0);
|
|
}
|
|
|
|
static void fdt_packblocks_(const char *old, char *new,
|
|
int mem_rsv_size,
|
|
int struct_size,
|
|
int strings_size)
|
|
{
|
|
int mem_rsv_off, struct_off, strings_off;
|
|
|
|
mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
|
|
struct_off = mem_rsv_off + mem_rsv_size;
|
|
strings_off = struct_off + struct_size;
|
|
|
|
memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
|
|
fdt_set_off_mem_rsvmap(new, mem_rsv_off);
|
|
|
|
memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
|
|
fdt_set_off_dt_struct(new, struct_off);
|
|
fdt_set_size_dt_struct(new, struct_size);
|
|
|
|
memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
|
|
fdt_set_off_dt_strings(new, strings_off);
|
|
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
|
|
}
|
|
|
|
int fdt_open_into(const void *fdt, void *buf, int bufsize)
|
|
{
|
|
int err;
|
|
int mem_rsv_size, struct_size;
|
|
int newsize;
|
|
const char *fdtstart = fdt;
|
|
const char *fdtend = fdtstart + fdt_totalsize(fdt);
|
|
char *tmp;
|
|
|
|
FDT_RO_PROBE(fdt);
|
|
|
|
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
|
* sizeof(struct fdt_reserve_entry);
|
|
|
|
if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
|
|
struct_size = fdt_size_dt_struct(fdt);
|
|
} else if (fdt_version(fdt) == 16) {
|
|
struct_size = 0;
|
|
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
|
|
;
|
|
if (struct_size < 0)
|
|
return struct_size;
|
|
} else {
|
|
return -FDT_ERR_BADVERSION;
|
|
}
|
|
|
|
if (can_assume(LIBFDT_ORDER) ||
|
|
!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
|
|
/* no further work necessary */
|
|
err = fdt_move(fdt, buf, bufsize);
|
|
if (err)
|
|
return err;
|
|
fdt_set_version(buf, 17);
|
|
fdt_set_size_dt_struct(buf, struct_size);
|
|
fdt_set_totalsize(buf, bufsize);
|
|
return 0;
|
|
}
|
|
|
|
/* Need to reorder */
|
|
newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
|
|
+ struct_size + fdt_size_dt_strings(fdt);
|
|
|
|
if (bufsize < newsize)
|
|
return -FDT_ERR_NOSPACE;
|
|
|
|
/* First attempt to build converted tree at beginning of buffer */
|
|
tmp = buf;
|
|
/* But if that overlaps with the old tree... */
|
|
if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
|
|
/* Try right after the old tree instead */
|
|
tmp = (char *)(uintptr_t)fdtend;
|
|
if ((tmp + newsize) > ((char *)buf + bufsize))
|
|
return -FDT_ERR_NOSPACE;
|
|
}
|
|
|
|
fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
|
|
fdt_size_dt_strings(fdt));
|
|
memmove(buf, tmp, newsize);
|
|
|
|
fdt_set_magic(buf, FDT_MAGIC);
|
|
fdt_set_totalsize(buf, bufsize);
|
|
fdt_set_version(buf, 17);
|
|
fdt_set_last_comp_version(buf, 16);
|
|
fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fdt_pack(void *fdt)
|
|
{
|
|
int mem_rsv_size;
|
|
|
|
FDT_RW_PROBE(fdt);
|
|
|
|
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
|
|
* sizeof(struct fdt_reserve_entry);
|
|
fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
|
|
fdt_size_dt_strings(fdt));
|
|
fdt_set_totalsize(fdt, fdt_data_size_(fdt));
|
|
|
|
return 0;
|
|
}
|