It is useful to be able to create a device tree from scratch using software. This is supported in libfdt but not currently available in the Python bindings. Add a new FdtSw class to handle this, with various methods corresponding to the libfdt functions. When the tree is complete, calling AsFdt() will return the completed device-tree object. Signed-off-by: Simon Glass <sjg@xxxxxxxxxxxx> --- pylibfdt/libfdt.i | 269 ++++++++++++++++++++++++++++++++++++++++ tests/pylibfdt_tests.py | 91 +++++++++++++- 2 files changed, 357 insertions(+), 3 deletions(-) diff --git a/pylibfdt/libfdt.i b/pylibfdt/libfdt.i index cf4ed93..36971b6 100644 --- a/pylibfdt/libfdt.i +++ b/pylibfdt/libfdt.i @@ -57,6 +57,17 @@ %{ #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(void *fdt, const char *name, const char *val, int len) +{ + return fdt_property(fdt, name, val, len); +} + %} %pythoncode %{ @@ -695,6 +706,251 @@ class Property(bytearray): def as_str(self): return self[:-1] + + +class FdtSw(object): + """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 finish() to complete the device tree so that it can + 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: + with FdtSw(small_size) as sw: + with sw.AddNode('node'): + sw.property_u32('reg', 2) + fdt = sw.AsFdt() + + # Now we can use it as a real device tree + fdt.setprop_u32(0, 'reg', 3) + """ + def __init__(self, size, quiet=()): + fdtrw = bytearray(size) + err = check_err(fdt_create(fdtrw, size)) + if err: + return err + self._fdtrw = fdtrw + + def __enter__(self): + """Contact manager to use to create a device tree via software""" + return self + + def __exit__(self, type, value, traceback): + check_err(fdt_finish(self._fdtrw)) + + def AsFdt(self): + """Convert a FdtSw into an Fdt so it can be accessed as normal + + Note that finish() must be called before this function will work. If + you are using the context manager (see 'with' code in the FdtSw class + comment) then this will happen automatically. + + Returns: + Fdt object allowing access to the newly created device tree + """ + return Fdt(self._fdtrw) + + def resize(self, size, quiet=()): + """Resize the buffer to accommodate a larger tree + + Args: + size: New size of tree + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + fdt = bytearray(size) + fdt[:len(self._fdtrw)] = self._fdtrw + err = check_err(fdt_resize(self._fdtrw, fdt, size), quiet) + if err: + return err + self._fdtrw = fdt + + def add_reservemap_entry(self, addr, size, quiet=()): + """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 + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_add_reservemap_entry(self._fdtrw, addr, size), + quiet) + + def finish_reservemap(self, quiet=()): + """Indicate that there are no more reserve map entries to add + + Args: + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_finish_reservemap(self._fdtrw), quiet) + + def begin_node(self, name, quiet=()): + """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 + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_begin_node(self._fdtrw, name), quiet) + + def property_string(self, name, string, quiet=()): + """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 + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_property_string(self._fdtrw, name, string), quiet) + + def property_u32(self, name, val, quiet=()): + """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 + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_property_u32(self._fdtrw, name, val), quiet) + + def property_u64(self, name, val, quiet=()): + """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 + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_property_u64(self._fdtrw, name, val), quiet) + + def property_cell(self, name, val, quiet=()): + """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 if no node found or other error occurs + """ + return check_err(fdt_property_cell(self._fdtrw, name, val), quiet) + + def property(self, name, val, quiet=()): + """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 + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(_fdt_property(self._fdtrw, name, val, len(val)), quiet) + + def end_node(self, quiet=()): + """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 if no node found or other error occurs + """ + return check_err(fdt_end_node(self._fdtrw), quiet) + + def finish(self, quiet=()): + """Finish writing the device tree + + This closes off the device tree ready for use + + Args: + quiet: Errors to ignore (empty to raise on all errors) + + Raises: + FdtException if no node found or other error occurs + """ + return check_err(fdt_finish(self._fdtrw), quiet) + + def AddNode(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._fdtrw, name) + + +class NodeAdder(): + """Class to provide a node context + + This allows you to add nodes in a more natural way: + + with fdtsw.AddNode('name'): + fdtsw.property_string('test', 'value') + + The node is automatically completed with a call to end_node() when the + context exits. + """ + def __init__(self, fdt, name): + self._fdt = fdt + self._name = name + + def __enter__(self): + check_err(fdt_begin_node(self._fdt, self._name)) + + def __exit__(self, type, value, traceback): + check_err(fdt_end_node(self._fdt)) %} %rename(fdt_property) fdt_property_func; @@ -755,6 +1011,11 @@ typedef int fdt32_t; $1 = PyString_AsString($input); /* char *str */ } +/* typemap used for fdt_add_reservemap_entry() */ +%typemap(in) uint64_t { + $1 = PyLong_AsUnsignedLong($input); +} + /* typemaps used for fdt_next_node() */ %typemap(in, numinputs=1) int *depth (int depth) { depth = (int) PyInt_AsLong($input); @@ -797,5 +1058,13 @@ 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); +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(void *fdt, const char *name, const char *val, int len); %include <../libfdt/libfdt.h> diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py index 57eedcb..61f064f 100644 --- a/tests/pylibfdt_tests.py +++ b/tests/pylibfdt_tests.py @@ -56,17 +56,34 @@ import unittest sys.path.insert(0, '../pylibfdt') import libfdt -from libfdt import Fdt, FdtException, QUIET_NOTFOUND, QUIET_ALL +from libfdt import Fdt, FdtSw, FdtException, QUIET_NOTFOUND, QUIET_ALL small_size = 160 full_size = 1024 +TEST_ADDR_1H = 0xdeadbeef +TEST_ADDR_1L = 0x00000000 +TEST_ADDR_1 = (TEST_ADDR_1H << 32) | TEST_ADDR_1L +TEST_ADDR_1 = 0x8000000000000000 +TEST_SIZE_1H = 0x00000000 +TEST_SIZE_1L = 0x00100000 +TEST_SIZE_1 = (TEST_SIZE_1H << 32) | TEST_SIZE_1L +TEST_ADDR_2H = 0 +TEST_ADDR_2L = 123456789 +TEST_ADDR_2 = (TEST_ADDR_2H << 32) | TEST_ADDR_2L +TEST_SIZE_2H = 0 +TEST_SIZE_2L = 010000 +TEST_SIZE_2 = (TEST_SIZE_2H << 32) | TEST_SIZE_2L TEST_VALUE_1 = 0xdeadbeef +TEST_VALUE_2 = 123456789 TEST_VALUE64_1H = 0xdeadbeef TEST_VALUE64_1L = 0x01abcdef TEST_VALUE64_1 = (TEST_VALUE64_1H << 32) | TEST_VALUE64_1L +PHANDLE_1 = 0x2000 +PHANDLE_2 = 0x2001 + TEST_STRING_1 = 'hello world' TEST_STRING_2 = 'hi world' @@ -93,8 +110,8 @@ def _ReadFdt(fname): """ return libfdt.Fdt(open(fname).read()) -class PyLibfdtTests(unittest.TestCase): - """Test class for pylibfdt +class PyLibfdtBasicTests(unittest.TestCase): + """Test class for basic pylibfdt access functions Properties: fdt: Device tree file used for testing @@ -458,5 +475,73 @@ class PyLibfdtTests(unittest.TestCase): self.assertEquals('test', self.fdt.get_name(node)) +class PyLibfdtSwTests(unittest.TestCase): + """Test class for pylibfdt sequential-write DT creation + """ + def assertOk(self, err_code): + self.assertEquals(0, err_code) + + def testCreate(self): + # First check the minimum size and also the Fdt() constructor + with self.assertRaisesRegexp(FdtException, get_err(libfdt.NOSPACE)): + self.assertEquals(-libfdt.NOSPACE, FdtSw(3)) + _= FdtSw(small_size) + + # Now use the context-manager version + with FdtSw(small_size) as sw: + sw.add_reservemap_entry(TEST_ADDR_1, TEST_SIZE_1) + sw.add_reservemap_entry(TEST_ADDR_2, TEST_SIZE_2) + sw.finish_reservemap() + + sw.begin_node('') + sw.property_string('compatible', 'test_tree1') + with self.assertRaisesRegexp(FdtException, get_err(libfdt.NOSPACE)): + sw.property_u32('prop-int', TEST_VALUE_1) + + # Check we can resize to its current size. Then make it bigger. + sw.resize(small_size) + sw.resize(full_size) + sw.property_u32('prop-int', TEST_VALUE_1) + sw.property_u64('prop-int64', TEST_VALUE64_1) + sw.property_string('prop-str', TEST_STRING_1) + sw.property_u32('#address-cells', 1) + sw.property_u32('#size-cells', 0) + + # Now that it is bigger we can't shrink it back + with self.assertRaisesRegexp(FdtException, get_err(libfdt.NOSPACE)): + sw.resize(small_size) + + sw.begin_node('subnode@1') + sw.property_string('compatible', 'subnode1') + sw.property_u32('reg', 1) + sw.property_cell('prop-int', TEST_VALUE_1) + sw.begin_node('subsubnode') + sw.property('compatible', 'subsubnode1\0subsubnode') + sw.property_cell('prop-int', TEST_VALUE_1) + sw.end_node() + sw.begin_node('ss1') + sw.end_node() + sw.end_node() + + with sw.AddNode('subnode@2'): + sw.property_u32('reg', 2) + sw.property_cell('linux,phandle', PHANDLE_1) + sw.property_cell('prop-int', TEST_VALUE_2) + sw.property_u32('#address-cells', 1) + sw.property_u32('#size-cells', 0) + with sw.AddNode('subsubnode@0'): + sw.property_u32('reg', 0) + sw.property_cell('phandle', PHANDLE_2) + sw.property('compatible', 'subsubnode2\0subsubnode') + sw.property_cell('prop-int', TEST_VALUE_2) + with sw.AddNode('ss2'): + pass + sw.end_node() + + fdt = sw.AsFdt() + self.assertEqual(2, fdt.num_mem_rsv()) + self.assertEqual([TEST_ADDR_1, TEST_SIZE_1], fdt.get_mem_rsv(0)) + + if __name__ == "__main__": unittest.main() -- 2.17.0.441.gb46fe60e1d-goog -- To unsubscribe from this list: send the line "unsubscribe devicetree-compiler" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html