summaryrefslogtreecommitdiff
path: root/scripts/dtc
diff options
context:
space:
mode:
authorMasahiro Yamada <yamada.masahiro@socionext.com>2017-10-17 13:42:43 +0900
committerTom Rini <trini@konsulko.com>2017-11-17 07:43:32 -0500
commit15b97f5c5e6d88e0560c6928f3acd01c999a494d (patch)
tree4b2416c2278d5a8c1ca3248a594d68753eea5673 /scripts/dtc
parent999a78d5cf00dfb8cd8342454933ea492e955377 (diff)
downloadu-boot-15b97f5c5e6d88e0560c6928f3acd01c999a494d.tar.gz
pylibfdt: move pylibfdt to scripts/dtc/pylibfdt and refactor makefile
The pylibfdt is used by dtoc (and, indirectly by binman), but there is no reason why it must be generated in the tools/ directory. Recently, U-Boot switched over to the bundled DTC, and the directory structure under scripts/dtc/ now mirrors the upstream DTC project. So, scripts/dtc/pylibfdt is the best location. I also rewrote the Makefile in a cleaner Kbuild style. The scripts from the upstream have been moved as follows: lib/libfdt/pylibfdt/setup.py -> scripts/dtc/pylibfdt/setup.py lib/libfdt/pylibfdt/libfdt.i -> scripts/dtc/pylibfdt/libfdt.i_shipped The .i_shipped is coped to .i during building because the .i must be located in the objtree when we build it out of tree. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Diffstat (limited to 'scripts/dtc')
-rw-r--r--scripts/dtc/Makefile3
-rw-r--r--scripts/dtc/pylibfdt/.gitignore4
-rw-r--r--scripts/dtc/pylibfdt/Makefile30
-rw-r--r--scripts/dtc/pylibfdt/libfdt.i_shipped449
-rwxr-xr-xscripts/dtc/pylibfdt/setup.py123
5 files changed, 609 insertions, 0 deletions
diff --git a/scripts/dtc/Makefile b/scripts/dtc/Makefile
index 2a48022c41..f4a16ed2a5 100644
--- a/scripts/dtc/Makefile
+++ b/scripts/dtc/Makefile
@@ -29,3 +29,6 @@ $(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h
# generated files need to be cleaned explicitly
clean-files := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h
+
+# Added for U-Boot
+subdir-y += pylibfdt
diff --git a/scripts/dtc/pylibfdt/.gitignore b/scripts/dtc/pylibfdt/.gitignore
new file mode 100644
index 0000000000..033f23dfdd
--- /dev/null
+++ b/scripts/dtc/pylibfdt/.gitignore
@@ -0,0 +1,4 @@
+/_libfdt.so
+/libfdt.py
+/libfdt.pyc
+/libfdt_wrap.c
diff --git a/scripts/dtc/pylibfdt/Makefile b/scripts/dtc/pylibfdt/Makefile
new file mode 100644
index 0000000000..01d5e0ffe3
--- /dev/null
+++ b/scripts/dtc/pylibfdt/Makefile
@@ -0,0 +1,30 @@
+# Unfortunately setup.py below cannot handle srctree being ".." which it often
+# is. It fails with an error like:
+# Fatal error: can't create build/temp.linux-x86_64-2.7/../lib/libfdt/fdt.o:
+# No such file or directory
+# To fix this, use an absolute path.
+LIBFDT_srcdir = $(abspath $(srctree)/$(src)/../libfdt)
+
+include $(LIBFDT_srcdir)/Makefile.libfdt
+
+# Unfortunately setup.py (or actually the Python distutil implementation) puts
+# files into the same directory as the .i file. We cannot touch the source
+# directory, so we "ship" .i file into the objtree.
+PYLIBFDT_srcs = $(addprefix $(LIBFDT_srcdir)/,$(LIBFDT_SRCS)) \
+ $(obj)/libfdt.i
+
+quiet_cmd_pymod = PYMOD $@
+ cmd_pymod = unset CC; unset CROSS_COMPILE; unset CFLAGS;\
+ LDFLAGS="$(HOSTLDFLAGS)" \
+ VERSION="u-boot-$(UBOOTVERSION)" \
+ CPPFLAGS="$(HOSTCFLAGS) -I$(LIBFDT_srcdir)" OBJDIR=$(obj) \
+ SOURCES="$(PYLIBFDT_srcs)" \
+ SWIG_OPTS="-I$(LIBFDT_srcdir) -I$(LIBFDT_srcdir)/.." \
+ $(PYTHON) $< --quiet build_ext --inplace
+
+$(obj)/_libfdt.so: $(src)/setup.py $(PYLIBFDT_srcs) FORCE
+ $(call if_changed,pymod)
+
+always += _libfdt.so
+
+clean-files += libfdt.i _libfdt.so libfdt.py libfdt_wrap.c
diff --git a/scripts/dtc/pylibfdt/libfdt.i_shipped b/scripts/dtc/pylibfdt/libfdt.i_shipped
new file mode 100644
index 0000000000..5b1a8cf4d4
--- /dev/null
+++ b/scripts/dtc/pylibfdt/libfdt.i_shipped
@@ -0,0 +1,449 @@
+/*
+ * pylibfdt - Flat Device Tree manipulation in Python
+ * Copyright (C) 2017 Google, Inc.
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
+ */
+
+%module libfdt
+
+%include <stdint.i>
+
+%{
+#define SWIG_FILE_WITH_INIT
+#include "libfdt.h"
+%}
+
+%pythoncode %{
+
+import struct
+
+# Error codes, corresponding to FDT_ERR_... in libfdt.h
+(NOTFOUND,
+ EXISTS,
+ NOSPACE,
+ BADOFFSET,
+ BADPATH,
+ BADPHANDLE,
+ BADSTATE,
+ TRUNCATED,
+ BADMAGIC,
+ BADVERSION,
+ BADSTRUCTURE,
+ BADLAYOUT,
+ INTERNAL,
+ BADNCELLS,
+ BADVALUE,
+ BADOVERLAY,
+ NOPHANDLES) = QUIET_ALL = range(1, 18)
+# QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
+# altogether. All # functions passed this value will return an error instead
+# of raising an exception.
+
+# Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
+# instead of raising an exception.
+QUIET_NOTFOUND = (NOTFOUND,)
+
+
+class FdtException(Exception):
+ """An exception caused by an error such as one of the codes above"""
+ def __init__(self, err):
+ self.err = err
+
+ def __str__(self):
+ return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
+
+def strerror(fdt_err):
+ """Get the string for an error number
+
+ Args:
+ fdt_err: Error number (-ve)
+
+ Returns:
+ String containing the associated error
+ """
+ return fdt_strerror(fdt_err)
+
+def check_err(val, quiet=()):
+ """Raise an error if the return value is -ve
+
+ This is used to check for errors returned by libfdt C functions.
+
+ Args:
+ val: Return value from a libfdt function
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ val if val >= 0
+
+ Raises
+ FdtException if val < 0
+ """
+ if val < 0:
+ if -val not in quiet:
+ raise FdtException(val)
+ return val
+
+def check_err_null(val, quiet=()):
+ """Raise an error if the return value is NULL
+
+ This is used to check for a NULL return value from certain libfdt C
+ functions
+
+ Args:
+ val: Return value from a libfdt function
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ val if val is a list, None if not
+
+ Raises
+ FdtException if val indicates an error was reported and the error
+ is not in @quiet.
+ """
+ # Normally a list is returned which contains the data and its length.
+ # If we get just an integer error code, it means the function failed.
+ if not isinstance(val, list):
+ if -val not in quiet:
+ raise FdtException(val)
+ return val
+
+class Fdt:
+ """Device tree class, supporting all operations
+
+ The Fdt object is created is created from a device tree binary file,
+ e.g. with something like:
+
+ fdt = Fdt(open("filename.dtb").read())
+
+ Operations can then be performed using the methods in this class. Each
+ method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
+
+ All methods raise an FdtException if an error occurs. To avoid this
+ behaviour a 'quiet' parameter is provided for some functions. This
+ defaults to empty, but you can pass a list of errors that you expect.
+ If one of these errors occurs, the function will return an error number
+ (e.g. -NOTFOUND).
+ """
+ def __init__(self, data):
+ self._fdt = bytearray(data)
+ check_err(fdt_check_header(self._fdt));
+
+ def subnode_offset(self, parentoffset, name, quiet=()):
+ """Get the offset of a named subnode
+
+ Args:
+ parentoffset: Offset of the parent node to check
+ name: Name of the required subnode, e.g. 'subnode@1'
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ The node offset of the found node, if any
+
+ Raises
+ FdtException if there is no node with that name, or other error
+ """
+ return check_err(fdt_subnode_offset(self._fdt, parentoffset, name),
+ quiet)
+
+ def path_offset(self, path, quiet=()):
+ """Get the offset for a given path
+
+ Args:
+ path: Path to the required node, e.g. '/node@3/subnode@1'
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ Node offset
+
+ Raises
+ FdtException if the path is not valid or not found
+ """
+ return check_err(fdt_path_offset(self._fdt, path), quiet)
+
+ def first_property_offset(self, nodeoffset, quiet=()):
+ """Get the offset of the first property in a node offset
+
+ Args:
+ nodeoffset: Offset to the node to check
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ Offset of the first property
+
+ Raises
+ FdtException if the associated node has no properties, or some
+ other error occurred
+ """
+ return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
+ quiet)
+
+ def next_property_offset(self, prop_offset, quiet=()):
+ """Get the next property in a node
+
+ Args:
+ prop_offset: Offset of the previous property
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ Offset of the next property
+
+ Raises:
+ FdtException if the associated node has no more properties, or
+ some other error occurred
+ """
+ return check_err(fdt_next_property_offset(self._fdt, prop_offset),
+ quiet)
+
+ def get_name(self, nodeoffset):
+ """Get the name of a node
+
+ Args:
+ nodeoffset: Offset of node to check
+
+ Returns:
+ Node name
+
+ Raises:
+ FdtException on error (e.g. nodeoffset is invalid)
+ """
+ return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
+
+ def get_property_by_offset(self, prop_offset, quiet=()):
+ """Obtains a property that can be examined
+
+ Args:
+ prop_offset: Offset of property (e.g. from first_property_offset())
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ Property object, or None if not found
+
+ Raises:
+ FdtException on error (e.g. invalid prop_offset or device
+ tree format)
+ """
+ pdata = check_err_null(
+ fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
+ if isinstance(pdata, (int)):
+ return pdata
+ return Property(pdata[0], pdata[1])
+
+ def first_subnode(self, nodeoffset, quiet=()):
+ """Find the first subnode of a parent node
+
+ Args:
+ nodeoffset: Node offset of parent node
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ The offset of the first subnode, if any
+
+ Raises:
+ FdtException if no subnode found or other error occurs
+ """
+ return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
+
+ def next_subnode(self, nodeoffset, quiet=()):
+ """Find the next subnode
+
+ Args:
+ nodeoffset: Node offset of previous subnode
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ The offset of the next subnode, if any
+
+ Raises:
+ FdtException if no more subnode found or other error occurs
+ """
+ return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
+
+ def totalsize(self):
+ """Return the total size of the device tree
+
+ Returns:
+ Total tree size in bytes
+ """
+ return check_err(fdt_totalsize(self._fdt))
+
+ def off_dt_struct(self):
+ """Return the start of the device tree struct area
+
+ Returns:
+ Start offset of struct area
+ """
+ return check_err(fdt_off_dt_struct(self._fdt))
+
+ def pack(self, quiet=()):
+ """Pack the device tree to remove unused space
+
+ This adjusts the tree in place.
+
+ Args:
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Raises:
+ FdtException if any error occurs
+ """
+ return check_err(fdt_pack(self._fdt), quiet)
+
+ def delprop(self, nodeoffset, prop_name):
+ """Delete a property from a node
+
+ Args:
+ nodeoffset: Node offset containing property to delete
+ prop_name: Name of property to delete
+
+ Raises:
+ FdtError if the property does not exist, or another error occurs
+ """
+ return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
+
+ def getprop(self, nodeoffset, prop_name, quiet=()):
+ """Get a property from a node
+
+ Args:
+ nodeoffset: Node offset containing property to get
+ prop_name: Name of property to get
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ Value of property as a bytearray, or -ve error number
+
+ Raises:
+ FdtError if any error occurs (e.g. the property is not found)
+ """
+ pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
+ quiet)
+ if isinstance(pdata, (int)):
+ return pdata
+ return bytearray(pdata[0])
+
+ def get_phandle(self, nodeoffset):
+ """Get the phandle of a node
+
+ Args:
+ nodeoffset: Node offset to check
+
+ Returns:
+ phandle of node, or 0 if the node has no phandle or another error
+ occurs
+ """
+ return fdt_get_phandle(self._fdt, nodeoffset)
+
+ def parent_offset(self, nodeoffset, quiet=()):
+ """Get the offset of a node's parent
+
+ Args:
+ nodeoffset: Node offset to check
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ The offset of the parent node, if any
+
+ Raises:
+ FdtException if no parent found or other error occurs
+ """
+ return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
+
+ def node_offset_by_phandle(self, phandle, quiet=()):
+ """Get the offset of a node with the given phandle
+
+ Args:
+ phandle: Phandle to search for
+ quiet: Errors to ignore (empty to raise on all errors)
+
+ Returns:
+ The offset of node with that phandle, if any
+
+ Raises:
+ FdtException if no node found or other error occurs
+ """
+ return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
+
+class Property:
+ """Holds a device tree property name and value.
+
+ This holds a copy of a property taken from the device tree. It does not
+ reference the device tree, so if anything changes in the device tree,
+ a Property object will remain valid.
+
+ Properties:
+ name: Property name
+ value: Proper value as a bytearray
+ """
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+%}
+
+%rename(fdt_property) fdt_property_func;
+
+typedef int fdt32_t;
+
+%include "libfdt/fdt.h"
+
+%include "typemaps.i"
+
+/* Most functions don't change the device tree, so use a const void * */
+%typemap(in) (const void *)(const void *fdt) {
+ if (!PyByteArray_Check($input)) {
+ SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
+ "', argument " "$argnum"" of type '" "$type""'");
+ }
+ $1 = (void *)PyByteArray_AsString($input);
+ fdt = $1;
+ fdt = fdt; /* avoid unused variable warning */
+}
+
+/* Some functions do change the device tree, so use void * */
+%typemap(in) (void *)(const void *fdt) {
+ if (!PyByteArray_Check($input)) {
+ SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
+ "', argument " "$argnum"" of type '" "$type""'");
+ }
+ $1 = PyByteArray_AsString($input);
+ fdt = $1;
+ fdt = fdt; /* avoid unused variable warning */
+}
+
+%typemap(out) (struct fdt_property *) {
+ PyObject *buff;
+
+ if ($1) {
+ resultobj = PyString_FromString(
+ 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);
+ }
+}
+
+%apply int *OUTPUT { int *lenp };
+
+/* typemap used for fdt_getprop() */
+%typemap(out) (const void *) {
+ if (!$1)
+ $result = Py_None;
+ else
+ $result = Py_BuildValue("s#", $1, *arg4);
+}
+
+/* We have both struct fdt_property and a function fdt_property() */
+%warnfilter(302) fdt_property;
+
+/* These are macros in the header so have to be redefined here */
+int fdt_magic(const void *fdt);
+int fdt_totalsize(const void *fdt);
+int fdt_off_dt_struct(const void *fdt);
+int fdt_off_dt_strings(const void *fdt);
+int fdt_off_mem_rsvmap(const void *fdt);
+int fdt_version(const void *fdt);
+int fdt_last_comp_version(const void *fdt);
+int fdt_boot_cpuid_phys(const void *fdt);
+int fdt_size_dt_strings(const void *fdt);
+int fdt_size_dt_struct(const void *fdt);
+
+%include <../libfdt/libfdt.h>
diff --git a/scripts/dtc/pylibfdt/setup.py b/scripts/dtc/pylibfdt/setup.py
new file mode 100755
index 0000000000..daf1089425
--- /dev/null
+++ b/scripts/dtc/pylibfdt/setup.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+"""
+setup.py file for SWIG libfdt
+Copyright (C) 2017 Google, Inc.
+Written by Simon Glass <sjg@chromium.org>
+
+SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
+
+Files to be built into the extension are provided in SOURCES
+C flags to use are provided in CPPFLAGS
+Object file directory is provided in OBJDIR
+Version is provided in VERSION
+
+If these variables are not given they are parsed from the Makefiles. This
+allows this script to be run stand-alone, e.g.:
+
+ ./pylibfdt/setup.py install [--prefix=...]
+"""
+
+from distutils.core import setup, Extension
+import os
+import re
+import sys
+
+# Decodes a Makefile assignment line into key and value (and plus for +=)
+RE_KEY_VALUE = re.compile('(?P<key>\w+) *(?P<plus>[+])?= *(?P<value>.*)$')
+
+
+def ParseMakefile(fname):
+ """Parse a Makefile to obtain its variables.
+
+ This collects variable assigments of the form:
+
+ VAR = value
+ VAR += more
+
+ It does not pick out := assignments, as these are not needed here. It does
+ handle line continuation.
+
+ Returns a dict:
+ key: Variable name (e.g. 'VAR')
+ value: Variable value (e.g. 'value more')
+ """
+ makevars = {}
+ with open(fname) as fd:
+ prev_text = '' # Continuation text from previous line(s)
+ for line in fd.read().splitlines():
+ if line and line[-1] == '\\': # Deal with line continuation
+ prev_text += line[:-1]
+ continue
+ elif prev_text:
+ line = prev_text + line
+ prev_text = '' # Continuation is now used up
+ m = RE_KEY_VALUE.match(line)
+ if m:
+ value = m.group('value') or ''
+ key = m.group('key')
+
+ # Appending to a variable inserts a space beforehand
+ if 'plus' in m.groupdict() and key in makevars:
+ makevars[key] += ' ' + value
+ else:
+ makevars[key] = value
+ return makevars
+
+def GetEnvFromMakefiles():
+ """Scan the Makefiles to obtain the settings we need.
+
+ This assumes that this script is being run from the top-level directory,
+ not the pylibfdt directory.
+
+ Returns:
+ Tuple with:
+ List of swig options
+ Version string
+ List of files to build
+ List of extra C preprocessor flags needed
+ Object directory to use (always '')
+ """
+ basedir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+ swig_opts = ['-I%s' % basedir]
+ makevars = ParseMakefile(os.path.join(basedir, 'Makefile'))
+ version = '%s.%s.%s' % (makevars['VERSION'], makevars['PATCHLEVEL'],
+ makevars['SUBLEVEL'])
+ makevars = ParseMakefile(os.path.join(basedir, 'libfdt', 'Makefile.libfdt'))
+ files = makevars['LIBFDT_SRCS'].split()
+ files = [os.path.join(basedir, 'libfdt', fname) for fname in files]
+ files.append('pylibfdt/libfdt.i')
+ cflags = ['-I%s' % basedir, '-I%s/libfdt' % basedir]
+ objdir = ''
+ return swig_opts, version, files, cflags, objdir
+
+
+progname = sys.argv[0]
+files = os.environ.get('SOURCES', '').split()
+cflags = os.environ.get('CPPFLAGS', '').split()
+objdir = os.environ.get('OBJDIR')
+version = os.environ.get('VERSION')
+swig_opts = os.environ.get('SWIG_OPTS', '').split()
+
+# If we were called directly rather than through our Makefile (which is often
+# the case with Python module installation), read the settings from the
+# Makefile.
+if not all((swig_opts, version, files, cflags, objdir)):
+ swig_opts, version, files, cflags, objdir = GetEnvFromMakefiles()
+
+libfdt_module = Extension(
+ '_libfdt',
+ sources = files,
+ extra_compile_args = cflags,
+ swig_opts = swig_opts,
+)
+
+setup(
+ name='libfdt',
+ version= version,
+ author='Simon Glass <sjg@chromium.org>',
+ description='Python binding for libfdt',
+ ext_modules=[libfdt_module],
+ package_dir={'': objdir},
+ py_modules=['pylibfdt/libfdt'],
+)