Re: [PATCH v2 10/10] pylibfdt: Support the sequential-write interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



On Wed, Jun 06, 2018 at 03:37:09PM -0600, Simon Glass wrote:
> 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>
> ---
> 
> Changes in v2: None
> 
>  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 566a401..6f9593d 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)

So, in C, names beginning with _ are reserved for the system, so
shouldn't be used for your own "internal functions" (the kernel gets
away with it because it doesn't use system libraries).

I've been moving away from leading _ and using trailing _ instead for
a while.

> +{
> +    return fdt_property(fdt, name, val, len);
> +}
> +
>  %}
>  
>  %pythoncode %{
> @@ -690,6 +701,251 @@ class Property(bytearray):
>      def __init__(self, name, value):
>          bytearray.__init__(self, value)
>          self.name = name
> +
> +
> +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

_fdtrw seems like a bad name to me, since it potentially confuses this
with the fdt_rw functions, which *can't* be used on sw trees.

> +
> +    def __enter__(self):
> +        """Contact manager to use to create a device tree via software"""

ITYM "context manager" and I'm not sure exactly what you're trying to
accomplish with this.

> +        return self
> +
> +    def __exit__(self, type, value, traceback):
> +        check_err(fdt_finish(self._fdtrw))
> +
> +    def AsFdt(self):

CamelCase isn't usual for Python methods.  I'd call it "as_fdt()".

> +        """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)

Having to explicitly call finish() then this seems a bit awkward.  How
about an as_fdt() which copies the in-progress, tree, finishes it,
then returns that as an Fdt object?

> +
> +    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

As with fdt_open_into(), fdt_resize() already copies the data so you
don't need to do it.

> +        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):

Again CamelCase wouldn't be usual here.

> +        """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;
> @@ -750,6 +1006,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);

Will swig do the right thing here if you pass in a Python int rather
than a Python long?

> +}
> +
>  /* typemaps used for fdt_next_node() */
>  %typemap(in, numinputs=1) int *depth (int depth) {
>     depth = (int) PyInt_AsLong($input);
> @@ -792,5 +1053,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 18a583d..2d132d9 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'
>  TEST_STRING_3 = u'unicode ' + unichr(467)
> @@ -94,8 +111,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
> @@ -479,5 +496,73 @@ class PyLibfdtTests(unittest.TestCase):
>          self.assertIn('embedded nul', str(e.exception))
>  
>  
> +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()

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux