Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e9a09a0
fdt: Add Python support for adding/removing nodes
sjg20 Sep 14, 2018
f2a7e50
dtoc: Allow syncing of the device tree back to a file
sjg20 Sep 14, 2018
d9a27c8
dtoc: Fixed endianness in Prop.GetEmpty()
sjg20 Sep 14, 2018
ea00b37
dtoc: Support adding new nodes
sjg20 Sep 14, 2018
eb5aa15
dtoc: Add methods for adding and updating properties
sjg20 Sep 14, 2018
c985e49
dtoc: Add a way to create an Fdt object from a data block
sjg20 Sep 14, 2018
03c444c
dtoc: Fix the value of SetInt()
sjg20 Oct 1, 2018
baa1daa
patman: Convert print statements to Python 3
sjg20 May 14, 2019
3c6bbc9
binman: Convert print statements to Python 3
sjg20 May 14, 2019
c8a308a
binman: Use items() instead of iteritems()
sjg20 May 14, 2019
275ca1d
tools: dtoc: Open all binary files in binary mode
sjg20 May 14, 2019
1796a79
binman: Handle repeated bytes for Python 3
sjg20 May 14, 2019
b7f3678
patman: Move unicode helpers to tools
sjg20 May 14, 2019
f4cbcb0
dtoc: Use GetBytes() to obtain repeating bytes
sjg20 May 18, 2019
abe6eaf
dtoc: Move BytesToValue() out of the Prop class
sjg20 May 18, 2019
6222f28
dtoc: Updates BytesToValue() for Python 3
sjg20 May 18, 2019
1828626
dtoc: Use byte type instead of str in fdt
sjg20 May 18, 2019
454d352
dtoc: Convert the Fdt.Prop class to Python 3
sjg20 May 18, 2019
df06a68
dtoc: Convert the Fdt.Node class to Python 3
sjg20 May 18, 2019
658eae2
dtoc: Update fdt_util for Python 3
sjg20 May 18, 2019
f23f501
pylibfdt: Convert to Python 3
sjg20 Oct 31, 2019
819f0b3
pylibfdt: Sync up with upstream
sjg20 Oct 31, 2019
23980c2
pylibfdt: Correct the type for fdt_property_stub()
sjg20 Oct 31, 2019
80f0a46
dtoc: Convert fdt.py to Python 3
sjg20 Oct 31, 2019
144e4ec
binman: Move to use Python 3
sjg20 Oct 31, 2019
110655f
binman: Adjust pylibfdt for incremental build
sjg20 Jul 10, 2020
c6939a7
libfdt: Tidy up pylibfdt build rule
sjg20 Mar 24, 2021
8ae594a
dtc: Update the build rule for pylibfdt
sjg20 Mar 27, 2021
fcd0439
libfdt: Fix invalid version warning
hramrach Oct 13, 2022
2f82613
fdt: Move to setuptools
sjg20 Jul 31, 2022
2b45736
libfdt: Fix build with python 3.10
hramrach Oct 13, 2022
0014a51
pylibfdt: Fix version normalization warning
Dec 15, 2022
73050ca
pylibfdt: Fix disable version normalization
Jan 4, 2023
576d11f
pylibfdt: Allow version normalization to fail
trini Jan 7, 2023
e28e5bf
pylibfdt: Fix "invalid escape sequence '\w'" in setup.py
Flowdalic Feb 20, 2024
621628b
scripts/dtc/pylibfdt/libfdt.i_shipped: Use SWIG_AppendOutput
MarkusVolk Oct 30, 2024
5f97132
binman: Backport binary input handling for Python 3
1715173329 Jul 26, 2026
31bb281
ci: switch to use Python 3
1715173329 Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .github/workflows/custom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,7 @@ jobs:
- name: Install essential packages
run: |
sudo apt-get update
sudo apt-get install swig
echo 'switch to python2 as default'
sudo rm /usr/bin/python
wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tar.xz
tar -xJf Python-2.7.18.tar.xz
cd Python-2.7.18
./configure && sudo make && sudo make install
sudo ln -s /usr/bin/python2 /usr/bin/python
sudo apt-get install swig python3-dev

- name: Download OpenWrt toolchain
run: |
Expand Down
106 changes: 106 additions & 0 deletions scripts/dtc/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
The source tree contains the Device Tree Compiler (dtc) toolchain for
working with device tree source and binary files and also libfdt, a
utility library for reading and manipulating the binary format.

DTC and LIBFDT are maintained by:

David Gibson <david@gibson.dropbear.id.au>
Jon Loeliger <loeliger@gmail.com>


Python library
--------------

A Python library is also available. To build this you will need to install
swig and Python development files. On Debian distributions:

sudo apt-get install swig python3-dev

The library provides an Fdt class which you can use like this:

$ PYTHONPATH=../pylibfdt python3
>>> import libfdt
>>> fdt = libfdt.Fdt(open('test_tree1.dtb', mode='rb').read())
>>> node = fdt.path_offset('/subnode@1')
>>> print(node)
124
>>> prop_offset = fdt.first_property_offset(node)
>>> prop = fdt.get_property_by_offset(prop_offset)
>>> print('%s=%s' % (prop.name, prop.as_str()))
compatible=subnode1
>>> node2 = fdt.path_offset('/')
>>> print(fdt.getprop(node2, 'compatible').as_str())
test_tree1

You will find tests in tests/pylibfdt_tests.py showing how to use each
method. Help is available using the Python help command, e.g.:

$ cd pylibfdt
$ python3 -c "import libfdt; help(libfdt)"

If you add new features, please check code coverage:

$ sudo apt-get install python3-coverage
$ cd tests
# It's just 'coverage' on most other distributions
$ python3-coverage run pylibfdt_tests.py
$ python3-coverage html
# Open 'htmlcov/index.html' in your browser


The library can be installed with pip from a local source tree:

pip install . [--user|--prefix=/path/to/install_dir]

Or directly from a remote git repo:

pip install git+git://git.kernel.org/pub/scm/utils/dtc/dtc.git@main

The install depends on libfdt shared library being installed on the host system
first. Generally, using --user or --prefix is not necessary and pip will use the
default location for the Python installation which varies if the user is root or
not.

You can also install everything via make if you like, but pip is recommended.

To install both libfdt and pylibfdt you can use:

make install [PREFIX=/path/to/install_dir]

To disable building the python library, even if swig and Python are available,
use:

make NO_PYTHON=1


More work remains to support all of libfdt, including access to numeric
values.


Adding a new function to libfdt.h
---------------------------------

The shared library uses libfdt/version.lds to list the exported functions, so
add your new function there. Check that your function works with pylibfdt. If
it cannot be supported, put the declaration in libfdt.h behind #ifndef SWIG so
that swig ignores it.


Tests
-----

Test files are kept in the tests/ directory. Use 'make check' to build and run
all tests.

If you want to adjust a test file, be aware that tree_tree1.dts is compiled
and checked against a binary tree from assembler macros in trees.S. So
if you change that file you must change tree.S also.


Mailing list
------------
The following list is for discussion about dtc and libfdt implementation
mailto:devicetree-compiler@vger.kernel.org

Core device tree bindings are discussed on the devicetree-spec list:
mailto:devicetree-spec@vger.kernel.org
21 changes: 17 additions & 4 deletions scripts/dtc/pylibfdt/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,32 @@ include $(LIBFDT_srcdir)/Makefile.libfdt
PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS)) \
$(obj)/libfdt.i

# create a version string compliant with PEP 440
PEP_VERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(subst -,,$(EXTRAVERSION))

quiet_cmd_pymod = PYMOD $@
cmd_pymod = unset CROSS_COMPILE; unset CFLAGS; \
CC="$(HOSTCC)" LDSHARED="$(HOSTCC) -shared " \
LDFLAGS="$(HOSTLDFLAGS)" \
VERSION="u-boot-$(UBOOTVERSION)" \
VERSION="$(PEP_VERSION)" \
CPPFLAGS="$(HOSTCFLAGS) -I$(LIBFDT_srcdir)" OBJDIR=$(obj) \
SOURCES="$(PYLIBFDT_srcs)" \
SWIG_OPTS="-I$(LIBFDT_srcdir) -I$(LIBFDT_srcdir)/.." \
$(PYTHON) $< --quiet build_ext --inplace
$(PYTHON3) $< --quiet build_ext --inplace

$(obj)/_libfdt.so: $(src)/setup.py $(PYLIBFDT_srcs) FORCE
rebuild: $(src)/setup.py $(PYLIBFDT_srcs)
@# Remove the library since otherwise Python doesn't seem to regenerate
@# the libfdt.py file if it is missing.
@rm -f $(obj)/_libfdt*.so
$(call if_changed,pymod)
@# Rename the file to _libfdt.so so this Makefile doesn't run every time
@if [ ! -e $(obj)/_libfdt.so ]; then \
mv $(obj)/_libfdt*.so $(obj)/_libfdt.so; \
fi

$(obj)/_libfdt.so $(obj)/libfdt.py &: rebuild
@:

always += _libfdt.so
always += _libfdt.so libfdt.py

clean-files += libfdt.i _libfdt.so libfdt.py libfdt_wrap.c
95 changes: 71 additions & 24 deletions scripts/dtc/pylibfdt/libfdt.i_shipped
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

%module libfdt

%begin %{
#define PY_SSIZE_T_CLEAN
%}

%include <stdint.i>

%{
Expand All @@ -18,7 +22,7 @@
* a struct called fdt_property. That struct causes swig to create a class in
* libfdt.py called fdt_property(), which confuses things.
*/
static int fdt_property_stub(void *fdt, const char *name, const char *val,
static int fdt_property_stub(void *fdt, const char *name, const void *val,
int len)
{
return fdt_property(fdt, name, val, len);
Expand Down Expand Up @@ -92,7 +96,7 @@ def check_err(val, quiet=()):
Raises
FdtException if val < 0
"""
if val < 0:
if isinstance(val, int) and val < 0:
if -val not in quiet:
raise FdtException(val)
return val
Expand Down Expand Up @@ -417,7 +421,7 @@ class FdtRo(object):
quiet)
if isinstance(pdata, (int)):
return pdata
return Property(prop_name, bytearray(pdata[0]))
return Property(prop_name, bytes(pdata[0]))

def get_phandle(self, nodeoffset):
"""Get the phandle of a node
Expand All @@ -431,6 +435,18 @@ class FdtRo(object):
"""
return fdt_get_phandle(self._fdt, nodeoffset)

def get_alias(self, name):
"""Get the full path referenced by a given alias

Args:
name: name of the alias to lookup

Returns:
Full path to the node for the alias named 'name', if it exists
None, if the given alias or the /aliases node does not exist
"""
return fdt_get_alias(self._fdt, name)

def parent_offset(self, nodeoffset, quiet=()):
"""Get the offset of a node's parent

Expand Down Expand Up @@ -624,32 +640,54 @@ class Fdt(FdtRo):
Raises:
FdtException if no parent found or other error occurs
"""
val = val.encode('utf-8') + '\0'
val = val.encode('utf-8') + b'\0'
return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name,
val, len(val)), quiet)

def delprop(self, nodeoffset, prop_name):
def delprop(self, nodeoffset, prop_name, quiet=()):
"""Delete a property from a node

Args:
nodeoffset: Node offset containing property to delete
prop_name: Name of property to delete
quiet: Errors to ignore (empty to raise on all errors)

Returns:
Error code, or 0 if OK

Raises:
FdtError if the property does not exist, or another error occurs
"""
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name), quiet)

def add_subnode(self, parentoffset, name, quiet=()):
"""Add a new subnode to a node

Args:
parentoffset: Parent offset to add the subnode to
name: Name of node to add

Returns:
offset of the node created, or negative error code on failure

Raises:
FdtError if there is not enough space, or another error occurs
"""
return check_err(fdt_add_subnode(self._fdt, parentoffset, name), quiet)

def del_node(self, nodeoffset):
def del_node(self, nodeoffset, quiet=()):
"""Delete a node

Args:
nodeoffset: Node offset containing property to delete
nodeoffset: Offset of node to delete

Returns:
Error code, or 0 if OK

Raises:
FdtError if the node does not exist, or another error occurs
FdtError if an error occurs
"""
return check_err(fdt_del_node(self._fdt, nodeoffset))
return check_err(fdt_del_node(self._fdt, nodeoffset), quiet)


class Property(bytearray):
Expand Down Expand Up @@ -705,8 +743,10 @@ class FdtSw(FdtRo):

# First create the device tree with a node and property:
sw = FdtSw()
with sw.add_node('node'):
sw.property_u32('reg', 2)
sw.finish_reservemap()
with sw.add_node(''):
with sw.add_node('node'):
sw.property_u32('reg', 2)
fdt = sw.as_fdt()

# Now we can use it as a real device tree
Expand Down Expand Up @@ -996,7 +1036,7 @@ typedef uint32_t fdt32_t;
fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
buff = PyByteArray_FromStringAndSize(
(const char *)($1 + 1), fdt32_to_cpu($1->len));
resultobj = SWIG_Python_AppendOutput(resultobj, buff);
resultobj = SWIG_AppendOutput(resultobj, buff);
}
}

Expand All @@ -1007,17 +1047,24 @@ typedef uint32_t fdt32_t;
if (!$1)
$result = Py_None;
else
$result = Py_BuildValue("s#", $1, *arg4);
%#if PY_VERSION_HEX >= 0x03000000
$result = Py_BuildValue("y#", $1, *arg4);
%#else
$result = Py_BuildValue("s#", $1, *arg4);
%#endif
}

/* typemap used for fdt_setprop() */
%typemap(in) (const void *val) {
$1 = PyString_AsString($input); /* char *str */
}

/* typemap used for fdt_add_reservemap_entry() */
%typemap(in) uint64_t {
$1 = PyLong_AsUnsignedLong($input);
%#if PY_VERSION_HEX >= 0x03000000
if (!PyBytes_Check($input)) {
SWIG_exception_fail(SWIG_TypeError, "bytes expected in method '" "$symname"
"', argument " "$argnum"" of type '" "$type""'");
}
$1 = PyBytes_AsString($input);
%#else
$1 = PyString_AsString($input); /* char *str */
%#endif
}

/* typemaps used for fdt_next_node() */
Expand All @@ -1028,7 +1075,7 @@ typedef uint32_t fdt32_t;

%typemap(argout) int *depth {
PyObject *val = Py_BuildValue("i", *arg$argnum);
resultobj = SWIG_Python_AppendOutput(resultobj, val);
resultobj = SWIG_AppendOutput(resultobj, val);
}

%apply int *depth { int *depth };
Expand All @@ -1039,12 +1086,12 @@ typedef uint32_t fdt32_t;
}

%typemap(argout) uint64_t * {
PyObject *val = PyLong_FromUnsignedLong(*arg$argnum);
PyObject *val = PyLong_FromUnsignedLongLong(*arg$argnum);
if (!result) {
if (PyTuple_GET_SIZE(resultobj) == 0)
resultobj = val;
else
resultobj = SWIG_Python_AppendOutput(resultobj, val);
resultobj = SWIG_AppendOutput(resultobj, val);
}
}

Expand All @@ -1070,6 +1117,6 @@ int fdt_property_cell(void *fdt, const char *name, uint32_t val);
* This function has a stub since the name fdt_property is used for both a
* function and a struct, which confuses SWIG.
*/
int fdt_property_stub(void *fdt, const char *name, const char *val, int len);
int fdt_property_stub(void *fdt, const char *name, const void *val, int len);

%include <../libfdt/libfdt.h>
Loading