Compare commits

...

7 commits

Author SHA1 Message Date
Rudi Heitbaum
f3451d1253 meson: Only take the version from dtc's own git repository
Some checks are pending
Build test / build-linux (make, fedora) (push) Waiting to run
Build test / build-linux (make, alpine) (push) Waiting to run
Build test / build-linux (make, archlinux) (push) Waiting to run
Build test / build-macos (make) (push) Waiting to run
Build test / build-linux (make, ubuntu) (push) Waiting to run
Build test / build-linux (meson, alpine) (push) Waiting to run
Build test / build-linux (meson, archlinux) (push) Waiting to run
Build test / build-linux (meson, fedora) (push) Waiting to run
Build test / build-linux (meson, ubuntu) (push) Waiting to run
Build test / build-macos (meson) (push) Waiting to run
Build test / mingw32 (push) Waiting to run
Build test / clang64 (push) Waiting to run
Build test / mingw64 (push) Waiting to run
Build test / ucrt64 (push) Waiting to run
Build test / FreeBSD 13.5 make build (push) Waiting to run
Build test / FreeBSD 14.3 make build (push) Waiting to run
Build test / FreeBSD 13.5 meson build (push) Waiting to run
Build test / FreeBSD 14.3 meson build (push) Waiting to run
When dtc is built from a release tarball unpacked inside another git
repository, git describe finds that repository and its tag ends up in the
version string, so dtc --version reports the tag rather than "DTC 1.8.1".
Point git at dtc's own .git, so that vcs_tag falls back to the project
version when there is none.

Signed-off-by: Rudi Heitbaum <rudi@heitbaum.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-10-07 15:45:24 +11:00
Héctor Orón Martínez
7a1e017926 libfdt: pass sources as keyword argument to library()
Some checks are pending
Build test / build-linux (make, alpine) (push) Waiting to run
Build test / build-linux (make, archlinux) (push) Waiting to run
Build test / build-linux (make, fedora) (push) Waiting to run
Build test / build-linux (make, ubuntu) (push) Waiting to run
Build test / build-linux (meson, alpine) (push) Waiting to run
Build test / build-linux (meson, archlinux) (push) Waiting to run
Build test / build-linux (meson, fedora) (push) Waiting to run
Build test / build-linux (meson, ubuntu) (push) Waiting to run
Build test / build-macos (make) (push) Waiting to run
Build test / build-macos (meson) (push) Waiting to run
Build test / mingw32 (push) Waiting to run
Build test / clang64 (push) Waiting to run
Build test / mingw64 (push) Waiting to run
Build test / ucrt64 (push) Waiting to run
Build test / FreeBSD 13.5 make build (push) Waiting to run
Build test / FreeBSD 14.3 make build (push) Waiting to run
Build test / FreeBSD 13.5 meson build (push) Waiting to run
Build test / FreeBSD 14.3 meson build (push) Waiting to run
Meson expects the source list for library() to be passed either as
positional arguments after the target name or via the 'sources:'
keyword. Using the implicit positional form can lead to malformed
linker commands on some toolchains, with spurious arguments such as
'libfdt' and '.' being treated as input files.

Explicitly pass the source list via 'sources: sources' and declare
the ABI version with 'soversion: '1'' to match the installed
libfdt.so.1 soname.

This fixes build failures where the linker reported:
  cannot find libfdt: file format not recognized
  cannot find .: file format not recognized

Signed-off-by: Héctor Orón Martínez <hector.oron@gmail.com>

Message-ID: <CAODfWeHQcjGn0CtweT4Q9Em6WC9cTmvfxhuQCgae4gdu5W9hSA@mail.gmail.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-09-08 21:11:38 +10:00
Herve Codina
8c37c14c95 fdtget: Use libfdt iterators instead of open coded loops
fdtget uses directly fdt_{first,next}_property_offset() with a while(1)
loop to iterate over node properties.

It also uses the low level primitive fdt_next_tag() with custom tags,
level and depth handling to iterates over subnodes.

It is worth noting that FDT_NOP can be returned by fdt_next_tag() and
this tag is not taken into account in the fdtget open coded loop. This
will lead to an incorrect error if a FDT_NOP tag is encountered.

libfdt provides iterators to iterate over node properties and subnodes.
The subnode iterator provided by libfdt is robust against FDT_NOP tags
and will be robust in the future when new tags are introduced.

Replace fdtget open coded loops by iterators provided by libfdt and
designed to perform those operations.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Message-ID: <20260826083146.304291-2-herve.codina@bootlin.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-08-27 13:48:09 +10:00
Alexey Charkov
89c99ce78a pylibfdt: Add add_mem_rsv() and del_mem_rsv()
The memory reserve map can be read through num_mem_rsv() and
get_mem_rsv(), but changing it requires calling the raw fdt_* wrappers
with the private Fdt._fdt buffer, as the class exposes no methods for
the write side.

Add them next to the existing accessors, and test them.

Signed-off-by: Alexey Charkov <alchark@flipper.net>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-08-08 19:06:53 +10:00
Alexey Charkov
3750493c8b pylibfdt: Add address_cells() and size_cells()
Finding out how many cells a node's children use for addresses and sizes
currently requires calling the raw fdt_address_cells()/fdt_size_cells()
wrappers with the private FdtRo._fdt buffer, as the class exposes no
methods for them.

Add them to FdtRo alongside the other node accessors, and test them.

Signed-off-by: Alexey Charkov <alchark@flipper.net>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-08-08 19:05:54 +10:00
Alexey Charkov
0748c384fd pylibfdt: Grow the FdtSw buffer geometrically
Every expansion copies the whole tree into a freshly allocated buffer, so
growing by a fixed amount makes building a tree cost time quadratic in
its size. This is especially painful when assembling larger images with
the data inline, such as U-Boot's binman does for FIT images.

Grow by at least as much as the tree already holds, which is what variable
sized arrays usually do specifically to avoid such excessive copying.

With this change, building a Rockchip TF-A+Falcon image whose FIT carries
a 31 MiB kernel takes 33.1 s rather than 44.4 s, with binman itself down
from 25.3 s to 14.0 s, as 7139 reallocations become 187. The images
produced are byte-identical.

Signed-off-by: Alexey Charkov <alchark@flipper.net>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-08-08 18:36:17 +10:00
Jitka Plesnikova
5008d1d6a3 pylibfdt: Replace removed SWIG Python 2 compatibility macros
SWIG 4.5.0 removed Python 2 compatibility macros (PyInt_*, PyString_*)
from its runtime header pyhead.swg (see commit 79f7a2b7cb7d). Replace
them with their Python 3 C API equivalents:

- PyString_FromString -> PyUnicode_FromString
- PyString_AsString -> PyBytes_AsString
- PyInt_AsLong -> PyLong_AsLong

The replacements are safe since the macros were already aliased to
these exact functions in SWIG's Python 3 code path.

Signed-off-by: Jitka Plesnikova <jplesnik@redhat.com>
Signed-off-by: Peter Robinson <pbrobinson@gmail.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-08-08 15:31:28 +10:00
5 changed files with 132 additions and 56 deletions

View file

@ -138,21 +138,20 @@ static int show_data(struct display_info *disp, const char *data, int len)
static int list_properties(const void *blob, int node) static int list_properties(const void *blob, int node)
{ {
const char *name; const char *name;
const void *p;
int prop; int prop;
prop = fdt_first_property_offset(blob, node); fdt_for_each_property_offset(prop, blob, node) {
do { p = fdt_getprop_by_offset(blob, prop, &name, NULL);
/* Stop silently when there are no more properties */ if (p && name)
if (prop < 0)
return prop == -FDT_ERR_NOTFOUND ? 0 : prop;
fdt_getprop_by_offset(blob, prop, &name, NULL);
if (name)
puts(name); puts(name);
prop = fdt_next_property_offset(blob, prop); }
} while (1);
}
#define MAX_LEVEL 32 /* how deeply nested we will go */ if ((prop < 0) && (prop != -FDT_ERR_NOTFOUND))
return prop;
return 0;
}
/** /**
* List all subnodes in a node, one per line * List all subnodes in a node, one per line
@ -163,47 +162,21 @@ static int list_properties(const void *blob, int node)
*/ */
static int list_subnodes(const void *blob, int node) static int list_subnodes(const void *blob, int node)
{ {
int nextoffset; /* next node offset from libfdt */
uint32_t tag; /* current tag */
int level = 0; /* keep track of nesting level */
const char *pathp; const char *pathp;
int depth = 1; /* the assumed depth of this node */ int subnode;
while (level >= 0) { fdt_for_each_subnode(subnode, blob, node) {
tag = fdt_next_tag(blob, node, &nextoffset); pathp = fdt_get_name(blob, subnode, NULL);
switch (tag) { if (pathp == NULL)
case FDT_BEGIN_NODE: pathp = "/* NULL pointer error */";
pathp = fdt_get_name(blob, node, NULL); if (*pathp == '\0')
if (level <= depth) { pathp = "/"; /* root is nameless */
if (pathp == NULL) puts(pathp);
pathp = "/* NULL pointer error */";
if (*pathp == '\0')
pathp = "/"; /* root is nameless */
if (level == 1)
puts(pathp);
}
level++;
if (level >= MAX_LEVEL) {
printf("Nested too deep, aborting.\n");
return 1;
}
break;
case FDT_END_NODE:
level--;
if (level == 0)
level = -1; /* exit the loop */
break;
case FDT_END:
return 1;
case FDT_PROP:
break;
default:
if (level <= depth)
printf("Unknown tag 0x%08X\n", tag);
return 1;
}
node = nextoffset;
} }
if (subnode < 0 && (subnode != -FDT_ERR_NOTFOUND))
return subnode;
return 0; return 0;
} }

View file

@ -27,8 +27,10 @@ endif
link_args += version_script link_args += version_script
libfdt = library( libfdt = library(
'fdt', sources, 'fdt',
sources: sources,
version: meson.project_version(), version: meson.project_version(),
soversion: '1',
link_args: link_args, link_args: link_args,
link_depends: 'version.lds', link_depends: 'version.lds',
install: get_option('default_library') != 'static' or not wheel_only, install: get_option('default_library') != 'static' or not wheel_only,

View file

@ -47,7 +47,7 @@ pylibfdt_enabled = not meson.is_cross_build() and py.found() and swig.found() ?
wheel_only = get_option('wheel-only') wheel_only = get_option('wheel-only')
version_gen_h = vcs_tag( version_gen_h = vcs_tag(
command: ['git', 'describe', '--dirty=+'], command: ['git', '--git-dir=.git', 'describe', '--dirty=+'],
input: 'version_gen.h.in', input: 'version_gen.h.in',
output: 'version_gen.h', output: 'version_gen.h',
) )

View file

@ -302,6 +302,38 @@ class FdtRo(object):
check_err(val[0], quiet) check_err(val[0], quiet)
return val[1:] return val[1:]
def address_cells(self, nodeoffset, quiet=()):
"""Return the number of address cells used by a node's children
Args:
nodeoffset: Offset of the node to check
quiet: Errors to ignore (empty to raise on all errors)
Returns:
Number of address cells used by the children of @nodeoffset
Raises:
FdtException if the node has an invalid #address-cells property,
or another error occurs
"""
return check_err(fdt_address_cells(self._fdt, nodeoffset), quiet)
def size_cells(self, nodeoffset, quiet=()):
"""Return the number of size cells used by a node's children
Args:
nodeoffset: Offset of the node to check
quiet: Errors to ignore (empty to raise on all errors)
Returns:
Number of size cells used by the children of @nodeoffset
Raises:
FdtException if the node has an invalid #size-cells property, or
another error occurs
"""
return check_err(fdt_size_cells(self._fdt, nodeoffset), quiet)
def subnode_offset(self, parentoffset, name, quiet=()): def subnode_offset(self, parentoffset, name, quiet=()):
"""Get the offset of a named subnode """Get the offset of a named subnode
@ -604,6 +636,42 @@ class Fdt(FdtRo):
del self._fdt[self.totalsize():] del self._fdt[self.totalsize():]
return err return err
def add_mem_rsv(self, addr, size, quiet=()):
"""Add a memory reserve-map record
This asks the client program not to use the given region of memory,
e.g. because something was loaded there.
Args:
addr: Start address of the region to reserve
size: Size of the region to reserve, in bytes
quiet: Errors to ignore (empty to raise on all errors)
Returns:
Error code, or 0 if OK
Raises:
FdtException if there is no space for another record, or another
error occurs
"""
return check_err(fdt_add_mem_rsv(self._fdt, addr, size), quiet)
def del_mem_rsv(self, index, quiet=()):
"""Remove the indexed memory reserve-map record
Args:
index: Record to remove (0=first)
quiet: Errors to ignore (empty to raise on all errors)
Returns:
Error code, or 0 if OK
Raises:
FdtException if there is no record at @index, or another error
occurs
"""
return check_err(fdt_del_mem_rsv(self._fdt, index), quiet)
def set_name(self, nodeoffset, name, quiet=()): def set_name(self, nodeoffset, name, quiet=()):
"""Set the name of a node """Set the name of a node
@ -857,7 +925,7 @@ class FdtSw(FdtRo):
device tree. This will be increased automatically as needed as new items device tree. This will be increased automatically as needed as new items
are added to the tree. are added to the tree.
""" """
INC_SIZE = 1024 # Expand size by this much when out of space INC_SIZE = 1024 # Expand size by at least this much when out of space
def __init__(self, size_hint=None): def __init__(self, size_hint=None):
"""Create a new FdtSw object """Create a new FdtSw object
@ -901,6 +969,10 @@ class FdtSw(FdtRo):
-NOSPACE then the FDT will be expanded to have more space, and True will -NOSPACE then the FDT will be expanded to have more space, and True will
be returned, indicating that the operation needs to be tried again. be returned, indicating that the operation needs to be tried again.
Each expansion copies the whole tree into a new buffer, so the size is
at least doubled rather than grown by a fixed amount, to keep the total
amount of copying proportional to the size of the tree.
Args: Args:
val: Return value from the operation that was attempted val: Return value from the operation that was attempted
@ -908,7 +980,7 @@ class FdtSw(FdtRo):
True if the operation must be retried, else False True if the operation must be retried, else False
""" """
if check_err(val, QUIET_NOSPACE) < 0: if check_err(val, QUIET_NOSPACE) < 0:
self.resize(len(self._fdt) + self.INC_SIZE) self.resize(len(self._fdt) + max(len(self._fdt), self.INC_SIZE))
return True return True
return False return False
@ -1136,7 +1208,7 @@ typedef uint32_t fdt32_t;
PyObject *buff; PyObject *buff;
if ($1) { if ($1) {
resultobj = PyString_FromString( resultobj = PyUnicode_FromString(
fdt_string(fdt1, fdt32_to_cpu($1->nameoff))); fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
buff = PyByteArray_FromStringAndSize( buff = PyByteArray_FromStringAndSize(
(const char *)($1 + 1), fdt32_to_cpu($1->len)); (const char *)($1 + 1), fdt32_to_cpu($1->len));
@ -1169,13 +1241,13 @@ typedef uint32_t fdt32_t;
} }
$1 = PyBytes_AsString($input); $1 = PyBytes_AsString($input);
%#else %#else
$1 = PyString_AsString($input); /* char *str */ $1 = PyBytes_AsString($input); /* char *str */
%#endif %#endif
} }
/* typemaps used for fdt_next_node() */ /* typemaps used for fdt_next_node() */
%typemap(in, numinputs=1) int *depth (int depth) { %typemap(in, numinputs=1) int *depth (int depth) {
depth = (int) PyInt_AsLong($input); depth = (int) PyLong_AsLong($input);
$1 = &depth; $1 = &depth;
} }

View file

@ -411,6 +411,35 @@ class PyLibfdtBasicTests(unittest.TestCase):
self.fdt.get_mem_rsv(0)) self.fdt.get_mem_rsv(0))
self.assertEqual([123456789, 0o10000], self.fdt.get_mem_rsv(1)) self.assertEqual([123456789, 0o10000], self.fdt.get_mem_rsv(1))
def testAddDelReserveMap(self):
"""Test that we can add to and remove from the memory reserve map"""
fdt = _ReadFdt('test_tree1.dtb')
fdt.resize(fdt.totalsize() + 64)
self.assertEqual(0, fdt.add_mem_rsv(0x40000000, 0x2000))
self.assertEqual(3, fdt.num_mem_rsv())
self.assertEqual([0x40000000, 0x2000], fdt.get_mem_rsv(2))
self.assertEqual(0, fdt.del_mem_rsv(2))
self.assertEqual(2, fdt.num_mem_rsv())
self.assertEqual(-libfdt.NOTFOUND,
fdt.del_mem_rsv(2, QUIET_NOTFOUND))
def testCells(self):
"""Test that we can read #address-cells and #size-cells"""
self.assertEqual(1, self.fdt.address_cells(0))
self.assertEqual(0, self.fdt.size_cells(0))
# A node without the properties inherits neither, so the libfdt
# defaults of 2 and 1 apply
node = self.fdt.path_offset('/subnode@1')
self.assertEqual(2, self.fdt.address_cells(node))
self.assertEqual(1, self.fdt.size_cells(node))
node = self.fdt.path_offset('/subnode@2')
self.assertEqual(1, self.fdt.address_cells(node))
self.assertEqual(0, self.fdt.size_cells(node))
def testEmpty(self): def testEmpty(self):
"""Test that we can create an empty tree""" """Test that we can create an empty tree"""
self.assertEqual(-libfdt.NOSPACE, self.assertEqual(-libfdt.NOSPACE,