diff options
Diffstat (limited to 'dtc/pylibfdt')
-rw-r--r-- | dtc/pylibfdt/.gitignore | 3 | ||||
-rw-r--r-- | dtc/pylibfdt/Makefile.pylibfdt | 30 | ||||
-rw-r--r-- | dtc/pylibfdt/libfdt.i | 1118 | ||||
-rw-r--r-- | dtc/pylibfdt/meson.build | 13 | ||||
-rwxr-xr-x | dtc/pylibfdt/setup.py | 59 |
5 files changed, 1223 insertions, 0 deletions
diff --git a/dtc/pylibfdt/.gitignore b/dtc/pylibfdt/.gitignore new file mode 100644 index 000000000..f63a8f136 --- /dev/null +++ b/dtc/pylibfdt/.gitignore @@ -0,0 +1,3 @@ +libfdt.py +*.pyc +libfdt_wrap.c diff --git a/dtc/pylibfdt/Makefile.pylibfdt b/dtc/pylibfdt/Makefile.pylibfdt new file mode 100644 index 000000000..1b5f23634 --- /dev/null +++ b/dtc/pylibfdt/Makefile.pylibfdt @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +# Makefile.pylibfdt +# + +PYLIBFDT_srcs = $(PYLIBFDT_dir)/libfdt.i +PYMODULE = $(PYLIBFDT_dir)/_libfdt.so +PYLIBFDT_CLEANFILES_L = libfdt_wrap.c libfdt.py *.pyc *.so +PYLIBFDT_CLEANFILES = $(PYLIBFDT_CLEANFILES_L:%=$(PYLIBFDT_dir)/%) +PYLIBFDT_CLEANDIRS_L = build __pycache__ +PYLIBFDT_CLEANDIRS = $(PYLIBFDT_CLEANDIRS_L:%=$(PYLIBFDT_dir)/%) + +SETUP = $(PYLIBFDT_dir)/setup.py +SETUPFLAGS = --top-builddir . + +ifndef V +SETUPFLAGS += --quiet +endif + +$(PYMODULE): $(PYLIBFDT_srcs) $(LIBFDT_archive) $(SETUP) $(VERSION_FILE) + @$(VECHO) PYMOD $@ + $(PYTHON) $(SETUP) $(SETUPFLAGS) build_ext --build-lib=$(PYLIBFDT_dir) + +install_pylibfdt: $(PYMODULE) + @$(VECHO) INSTALL-PYLIB + $(PYTHON) $(SETUP) $(SETUPFLAGS) install --prefix=$(PREFIX) + +pylibfdt_clean: + @$(VECHO) CLEAN "(pylibfdt)" + rm -f $(PYLIBFDT_CLEANFILES) + rm -rf $(PYLIBFDT_CLEANDIRS) diff --git a/dtc/pylibfdt/libfdt.i b/dtc/pylibfdt/libfdt.i new file mode 100644 index 000000000..51ee8014d --- /dev/null +++ b/dtc/pylibfdt/libfdt.i @@ -0,0 +1,1118 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * pylibfdt - Flat Device Tree manipulation in Python + * Copyright (C) 2017 Google, Inc. + * Written by Simon Glass <sjg@chromium.org> + */ + +%module libfdt + +%include <stdint.i> + +%{ +#define SWIG_FILE_WITH_INIT +#include "libfdt.h" + +/* + * We rename this function here to avoid problems with swig, since we also have + * 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 void *val, + int len) +{ + return fdt_property(fdt, name, val, len); +} + +%} + +%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,) +QUIET_NOSPACE = (NOSPACE,) + + +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 isinstance(val, int) and 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 FdtRo(object): + """Class for a read-only device-tree + + This is a base class used by FdtRw (read-write access) and FdtSw + (sequential-write access). It implements read-only access to the + device tree. + + Here are the three classes and when you should use them: + + FdtRo - read-only access to an existing FDT + FdtRw - read-write access to an existing FDT (most common case) + FdtSw - for creating a new FDT, as well as allowing read-only access + """ + def __init__(self, data): + self._fdt = bytearray(data) + check_err(fdt_check_header(self._fdt)); + + def as_bytearray(self): + """Get the device tree contents as a bytearray + + This can be passed directly to libfdt functions that access a + const void * for the device tree. + + Returns: + bytearray containing the device tree + """ + return bytearray(self._fdt) + + def next_node(self, nodeoffset, depth, quiet=()): + """Find the next subnode + + Args: + nodeoffset: Node offset of previous node + depth: The depth of the node at nodeoffset. This is used to + calculate the depth of the returned node + quiet: Errors to ignore (empty to raise on all errors) + + Returns: + Typle: + Offset of the next node, if any, else a -ve error + Depth of the returned node, if any, else undefined + + Raises: + FdtException if no more nodes found or other error occurs + """ + return check_err(fdt_next_node(self._fdt, nodeoffset, depth), quiet) + + 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 subnodes 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 subnodes found or other error occurs + """ + return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet) + + def magic(self): + """Return the magic word from the header + + Returns: + Magic word + """ + return fdt_magic(self._fdt) + + def totalsize(self): + """Return the total size of the device tree + + Returns: + Total tree size in bytes + """ + return 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 fdt_off_dt_struct(self._fdt) + + def off_dt_strings(self): + """Return the start of the device-tree string area + + Returns: + Start offset of string area + """ + return fdt_off_dt_strings(self._fdt) + + def off_mem_rsvmap(self): + """Return the start of the memory reserve map + + Returns: + Start offset of memory reserve map + """ + return fdt_off_mem_rsvmap(self._fdt) + + def version(self): + """Return the version of the device tree + + Returns: + Version number of the device tree + """ + return fdt_version(self._fdt) + + def last_comp_version(self): + """Return the last compatible version of the device tree + + Returns: + Last compatible version number of the device tree + """ + return fdt_last_comp_version(self._fdt) + + def boot_cpuid_phys(self): + """Return the physical boot CPU ID + + Returns: + Physical boot CPU ID + """ + return fdt_boot_cpuid_phys(self._fdt) + + def size_dt_strings(self): + """Return the start of the device-tree string area + + Returns: + Start offset of string area + """ + return fdt_size_dt_strings(self._fdt) + + def size_dt_struct(self): + """Return the start of the device-tree struct area + + Returns: + Start offset of struct area + """ + return fdt_size_dt_struct(self._fdt) + + def num_mem_rsv(self, quiet=()): + """Return the number of memory reserve-map records + + Returns: + Number of memory reserve-map records + """ + return check_err(fdt_num_mem_rsv(self._fdt), quiet) + + def get_mem_rsv(self, index, quiet=()): + """Return the indexed memory reserve-map record + + Args: + index: Record to return (0=first) + + Returns: + Number of memory reserve-map records + """ + return check_err(fdt_get_mem_rsv(self._fdt, index), quiet) + + 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 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 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_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 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 Property object (which can be used as a + bytearray/string), or -ve error number. On failure, returns an + integer error + + 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 Property(prop_name, 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 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 + + 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 Fdt(FdtRo): + """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): + FdtRo.__init__(self, data) + + @staticmethod + def create_empty_tree(size, quiet=()): + """Create an empty device tree ready for use + + Args: + size: Size of device tree in bytes + + Returns: + Fdt object containing the device tree + """ + data = bytearray(size) + err = check_err(fdt_create_empty_tree(data, size), quiet) + if err: + return err + return Fdt(data) + + def resize(self, size, quiet=()): + """Move the device tree into a larger or smaller space + + This creates a new device tree of size @size and moves the existing + device tree contents over to that. It can be used to create more space + in a device tree. Note that the Fdt object remains the same, but it + now has a new bytearray holding the contents. + + Args: + size: Required new size of device tree in bytes + """ + fdt = bytearray(size) + err = check_err(fdt_open_into(self._fdt, fdt, size), quiet) + if err: + return err + self._fdt = 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) + + Returns: + Error code, or 0 if OK + + Raises: + FdtException if any error occurs + """ + err = check_err(fdt_pack(self._fdt), quiet) + if err: + return err + del self._fdt[self.totalsize():] + return err + + def set_name(self, nodeoffset, name, quiet=()): + """Set the name of a node + + Args: + nodeoffset: Node offset of node to update + name: New node name (string without \0) + + Returns: + Error code, or 0 if OK + + Raises: + FdtException if no parent found or other error occurs + """ + if chr(0) in name: + raise ValueError('Property contains embedded nul characters') + return check_err(fdt_set_name(self._fdt, nodeoffset, name), quiet) + + def setprop(self, nodeoffset, prop_name, val, quiet=()): + """Set the value of a property + + Args: + nodeoffset: Node offset containing the property to create/update + prop_name: Name of property + val: Value to write (string or bytearray) + quiet: Errors to ignore (empty to raise on all errors) + + Returns: + Error code, or 0 if OK + + Raises: + FdtException if no parent found or other error occurs + """ + return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name, val, + len(val)), quiet) + + def setprop_u32(self, nodeoffset, prop_name, val, quiet=()): + """Set the value of a property + + Args: + nodeoffset: Node offset containing the property to create/update + prop_name: Name of property + val: Value to write (integer) + quiet: Errors to ignore (empty to raise on all errors) + + Returns: + Error code, or 0 if OK + + Raises: + FdtException if no parent found or other error occurs + """ + return check_err(fdt_setprop_u32(self._fdt, nodeoffset, prop_name, val), + quiet) + + def setprop_u64(self, nodeoffset, prop_name, val, quiet=()): + """Set the value of a property + + Args: + nodeoffset: Node offset containing the property to create/update + prop_name: Name of property + val: Value to write (integer) + quiet: Errors to ignore (empty to raise on all errors) + + Returns: + Error code, or 0 if OK + + Raises: + FdtException if no parent found or other error occurs + """ + return check_err(fdt_setprop_u64(self._fdt, nodeoffset, prop_name, val), + quiet) + + def setprop_str(self, nodeoffset, prop_name, val, quiet=()): + """Set the string value of a property + + The property is set to the string, with a nul terminator added + + Args: + nodeoffset: Node offset containing the property to create/update + prop_name: Name of property + val: Value to write (string without nul terminator). Unicode is + supposed by encoding to UTF-8 + quiet: Errors to ignore (empty to raise on all errors) + + Returns: + Error code, or 0 if OK + + Raises: + FdtException if no parent found or other error occurs + """ + 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, 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), 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, quiet=()): + """Delete a node + + Args: + nodeoffset: Offset of node to delete + + Returns: + Error code, or 0 if OK + + Raises: + FdtError if an error occurs + """ + return check_err(fdt_del_node(self._fdt, nodeoffset), quiet) + + +class Property(bytearray): + """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: Property value as a bytearray + """ + def __init__(self, name, value): + bytearray.__init__(self, value) + self.name = name + + def as_cell(self, fmt): + return struct.unpack('>' + fmt, self)[0] + + def as_uint32(self): + return self.as_cell('L') + + def as_int32(self): + return self.as_cell('l') + + def as_uint64(self): + return self.as_cell('Q') + + def as_int64(self): + return self.as_cell('q') + + def as_str(self): + """Unicode is supported by decoding from UTF-8""" + if self[-1] != 0: + raise ValueError('Property lacks nul termination') + if 0 in self[:-1]: + raise ValueError('Property contains embedded nul characters') + return self[:-1].decode('utf-8') + + +class FdtSw(FdtRo): + """Software interface to create a device tree from scratch + + The methods in this class work by adding to an existing 'partial' device + tree buffer of a fixed size created by instantiating this class. When the + tree is complete, call as_fdt() to obtain a device tree ready to be used. + + Similarly with nodes, a new node is started with begin_node() and finished + with end_node(). + + The context manager functions can be used to make this a bit easier: + + # First create the device tree with a node and property: + sw = FdtSw() + 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 + fdt.setprop_u32(0, 'reg', 3) + + The size hint provides a starting size for the space to be used by the + device tree. This will be increased automatically as needed as new items + are added to the tree. + """ + INC_SIZE = 1024 # Expand size by this much when out of space + + def __init__(self, size_hint=None): + """Create a new FdtSw object + + Args: + size_hint: A hint as to the initial size to use + + Raises: + ValueError if size_hint is negative + + Returns: + FdtSw object on success, else integer error code (if not raising) + """ + if not size_hint: + size_hint = self.INC_SIZE + fdtsw = bytearray(size_hint) + err = check_err(fdt_create(fdtsw, size_hint)) + if err: + return err + self._fdt = fdtsw + + def as_fdt(self): + """Convert a FdtSw into an Fdt so it can be accessed as normal + + Creates a new Fdt object from the work-in-progress device tree. This + does not call fdt_finish() on the current object, so it is possible to + add more nodes/properties and call as_fdt() again to get an updated + tree. + + Returns: + Fdt object allowing access to the newly created device tree + """ + fdtsw = bytearray(self._fdt) + check_err(fdt_finish(fdtsw)) + return Fdt(fdtsw) + + def check_space(self, val): + """Check if we need to add more space to the FDT + + This should be called with the error code from an operation. If this is + -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. + + Args: + val: Return value from the operation that was attempted + + Returns: + True if the operation must be retried, else False + """ + if check_err(val, QUIET_NOSPACE) < 0: + self.resize(len(self._fdt) + self.INC_SIZE) + return True + return False + + def resize(self, size): + """Resize the buffer to accommodate a larger tree + + Args: + size: New size of tree + + Raises: + FdtException on any error + """ + fdt = bytearray(size) + err = check_err(fdt_resize(self._fdt, fdt, size)) + self._fdt = fdt + + def add_reservemap_entry(self, addr, size): + """Add a new memory reserve map entry + + Once finished adding, you must call finish_reservemap(). + + Args: + addr: 64-bit start address + size: 64-bit size + + Raises: + FdtException on any error + """ + while self.check_space(fdt_add_reservemap_entry(self._fdt, addr, + size)): + pass + + def finish_reservemap(self): + """Indicate that there are no more reserve map entries to add + + Raises: + FdtException on any error + """ + while self.check_space(fdt_finish_reservemap(self._fdt)): + pass + + def begin_node(self, name): + """Begin a new node + + Use this before adding properties to the node. Then call end_node() to + finish it. You can also use the context manager as shown in the FdtSw + class comment. + + Args: + name: Name of node to begin + + Raises: + FdtException on any error + """ + while self.check_space(fdt_begin_node(self._fdt, name)): + pass + + def property_string(self, name, string): + """Add a property with a string value + + The string will be nul-terminated when written to the device tree + + Args: + name: Name of property to add + string: String value of property + + Raises: + FdtException on any error + """ + while self.check_space(fdt_property_string(self._fdt, name, string)): + pass + + def property_u32(self, name, val): + """Add a property with a 32-bit value + + Write a single-cell value to the device tree + + Args: + name: Name of property to add + val: Value of property + + Raises: + FdtException on any error + """ + while self.check_space(fdt_property_u32(self._fdt, name, val)): + pass + + def property_u64(self, name, val): + """Add a property with a 64-bit value + + Write a double-cell value to the device tree in big-endian format + + Args: + name: Name of property to add + val: Value of property + + Raises: + FdtException on any error + """ + while self.check_space(fdt_property_u64(self._fdt, name, val)): + pass + + def property_cell(self, name, val): + """Add a property with a single-cell value + + Write a single-cell value to the device tree + + Args: + name: Name of property to add + val: Value of property + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException on any error + """ + while self.check_space(fdt_property_cell(self._fdt, name, val)): + pass + + def property(self, name, val): + """Add a property + + Write a new property with the given value to the device tree. The value + is taken as is and is not nul-terminated + + Args: + name: Name of property to add + val: Value of property (bytes) + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException on any error + """ + while self.check_space(fdt_property_stub(self._fdt, name, val, + len(val))): + pass + + def end_node(self): + """End a node + + Use this after adding properties to a node to close it off. You can also + use the context manager as shown in the FdtSw class comment. + + Args: + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException on any error + """ + while self.check_space(fdt_end_node(self._fdt)): + pass + + def add_node(self, name): + """Create a new context for adding a node + + When used in a 'with' clause this starts a new node and finishes it + afterward. + + Args: + name: Name of node to add + """ + return NodeAdder(self, name) + + +class NodeAdder(): + """Class to provide a node context + + This allows you to add nodes in a more natural way: + + with fdtsw.add_node('name'): + fdtsw.property_string('test', 'value') + + The node is automatically completed with a call to end_node() when the + context exits. + """ + def __init__(self, fdtsw, name): + self._fdt = fdtsw + self._name = name + + def __enter__(self): + self._fdt.begin_node(self._name) + + def __exit__(self, type, value, traceback): + self._fdt.end_node() +%} + +%rename(fdt_property) fdt_property_func; + +/* + * fdt32_t is a big-endian 32-bit value defined to uint32_t in libfdt_env.h + * so use the same type here. + */ +typedef uint32_t fdt32_t; + +%include "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; + (void)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; + (void)fdt; /* avoid unused variable warning */ +} + +/* typemap used for fdt_get_property_by_offset() */ +%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 + %#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) { + %#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() */ +%typemap(in, numinputs=1) int *depth (int depth) { + depth = (int) PyInt_AsLong($input); + $1 = &depth; +} + +%typemap(argout) int *depth { + PyObject *val = Py_BuildValue("i", *arg$argnum); + resultobj = SWIG_Python_AppendOutput(resultobj, val); +} + +%apply int *depth { int *depth }; + +/* typemaps for fdt_get_mem_rsv */ +%typemap(in, numinputs=0) uint64_t * (uint64_t temp) { + $1 = &temp; +} + +%typemap(argout) uint64_t * { + PyObject *val = PyLong_FromUnsignedLongLong(*arg$argnum); + if (!result) { + if (PyTuple_GET_SIZE(resultobj) == 0) + resultobj = val; + else + resultobj = SWIG_Python_AppendOutput(resultobj, val); + } +} + +/* 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 */ +uint32_t fdt_magic(const void *fdt); +uint32_t fdt_totalsize(const void *fdt); +uint32_t fdt_off_dt_struct(const void *fdt); +uint32_t fdt_off_dt_strings(const void *fdt); +uint32_t fdt_off_mem_rsvmap(const void *fdt); +uint32_t fdt_version(const void *fdt); +uint32_t fdt_last_comp_version(const void *fdt); +uint32_t fdt_boot_cpuid_phys(const void *fdt); +uint32_t fdt_size_dt_strings(const void *fdt); +uint32_t fdt_size_dt_struct(const void *fdt); + +int fdt_property_string(void *fdt, const char *name, const char *val); +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 void *val, int len); + +%include <libfdt.h> diff --git a/dtc/pylibfdt/meson.build b/dtc/pylibfdt/meson.build new file mode 100644 index 000000000..088f24934 --- /dev/null +++ b/dtc/pylibfdt/meson.build @@ -0,0 +1,13 @@ +setup_py = find_program('setup.py') +setup_py = [setup_py.path(), '--quiet', '--top-builddir', meson.current_build_dir() / '..'] + +custom_target( + 'pylibfdt', + input: 'libfdt.i', + output: '_libfdt.so', + depends: version_gen_h, + command: [setup_py, 'build_ext', '--build-lib=' + meson.current_build_dir()], + build_by_default: true, +) + +meson.add_install_script(setup_py, 'install', '--prefix=' + get_option('prefix'), '--root=$DESTDIR') diff --git a/dtc/pylibfdt/setup.py b/dtc/pylibfdt/setup.py new file mode 100755 index 000000000..ef40f1542 --- /dev/null +++ b/dtc/pylibfdt/setup.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) + +# While Python 3 is the default, it's also possible to invoke +# this setup.py script with Python 2. + +""" +setup.py file for SWIG libfdt +Copyright (C) 2017 Google, Inc. +Written by Simon Glass <sjg@chromium.org> +""" + +from distutils.core import setup, Extension +import os +import re +import sys + + +VERSION_PATTERN = '^#define DTC_VERSION "DTC ([^"]*)"$' + + +def get_top_builddir(): + if '--top-builddir' in sys.argv: + index = sys.argv.index('--top-builddir') + sys.argv.pop(index) + return sys.argv.pop(index) + else: + return os.getcwd() + + +srcdir = os.path.dirname(os.path.abspath(sys.argv[0])) +top_builddir = get_top_builddir() + + +def get_version(): + version_file = os.path.join(top_builddir, 'version_gen.h') + f = open(version_file, 'rt') + m = re.match(VERSION_PATTERN, f.readline()) + return m.group(1) + + +libfdt_module = Extension( + '_libfdt', + sources=[os.path.join(srcdir, 'libfdt.i')], + include_dirs=[os.path.join(srcdir, '../libfdt')], + libraries=['fdt'], + library_dirs=[os.path.join(top_builddir, 'libfdt')], + swig_opts=['-I' + os.path.join(srcdir, '../libfdt')], +) + +setup( + name='libfdt', + version=get_version(), + author='Simon Glass <sjg@chromium.org>', + description='Python binding for libfdt', + ext_modules=[libfdt_module], + package_dir={'': srcdir}, + py_modules=['libfdt'], +) |