Home | History | Annotate | Download | only in pylibfdt
      1 /*
      2  * pylibfdt - Flat Device Tree manipulation in Python
      3  * Copyright (C) 2017 Google, Inc.
      4  * Written by Simon Glass <sjg (at) chromium.org>
      5  *
      6  * libfdt is dual licensed: you can use it either under the terms of
      7  * the GPL, or the BSD license, at your option.
      8  *
      9  *  a) This library is free software; you can redistribute it and/or
     10  *     modify it under the terms of the GNU General Public License as
     11  *     published by the Free Software Foundation; either version 2 of the
     12  *     License, or (at your option) any later version.
     13  *
     14  *     This library is distributed in the hope that it will be useful,
     15  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  *     GNU General Public License for more details.
     18  *
     19  *     You should have received a copy of the GNU General Public
     20  *     License along with this library; if not, write to the Free
     21  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
     22  *     MA 02110-1301 USA
     23  *
     24  * Alternatively,
     25  *
     26  *  b) Redistribution and use in source and binary forms, with or
     27  *     without modification, are permitted provided that the following
     28  *     conditions are met:
     29  *
     30  *     1. Redistributions of source code must retain the above
     31  *        copyright notice, this list of conditions and the following
     32  *        disclaimer.
     33  *     2. Redistributions in binary form must reproduce the above
     34  *        copyright notice, this list of conditions and the following
     35  *        disclaimer in the documentation and/or other materials
     36  *        provided with the distribution.
     37  *
     38  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
     39  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
     40  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     41  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     42  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     43  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     44  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     45  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     46  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     47  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     48  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     49  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     50  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     51  */
     52 
     53 %module libfdt
     54 
     55 %{
     56 #define SWIG_FILE_WITH_INIT
     57 #include "libfdt.h"
     58 %}
     59 
     60 %pythoncode %{
     61 
     62 import struct
     63 
     64 # Error codes, corresponding to FDT_ERR_... in libfdt.h
     65 (NOTFOUND,
     66         EXISTS,
     67         NOSPACE,
     68         BADOFFSET,
     69         BADPATH,
     70         BADPHANDLE,
     71         BADSTATE,
     72         TRUNCATED,
     73         BADMAGIC,
     74         BADVERSION,
     75         BADSTRUCTURE,
     76         BADLAYOUT,
     77         INTERNAL,
     78         BADNCELLS,
     79         BADVALUE,
     80         BADOVERLAY,
     81         NOPHANDLES) = QUIET_ALL = range(1, 18)
     82 # QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
     83 # altogether. All # functions passed this value will return an error instead
     84 # of raising an exception.
     85 
     86 # Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
     87 # instead of raising an exception.
     88 QUIET_NOTFOUND = (NOTFOUND,)
     89 
     90 
     91 class FdtException(Exception):
     92     """An exception caused by an error such as one of the codes above"""
     93     def __init__(self, err):
     94         self.err = err
     95 
     96     def __str__(self):
     97         return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
     98 
     99 def strerror(fdt_err):
    100     """Get the string for an error number
    101 
    102     Args:
    103         fdt_err: Error number (-ve)
    104 
    105     Returns:
    106         String containing the associated error
    107     """
    108     return fdt_strerror(fdt_err)
    109 
    110 def check_err(val, quiet=()):
    111     """Raise an error if the return value is -ve
    112 
    113     This is used to check for errors returned by libfdt C functions.
    114 
    115     Args:
    116         val: Return value from a libfdt function
    117         quiet: Errors to ignore (empty to raise on all errors)
    118 
    119     Returns:
    120         val if val >= 0
    121 
    122     Raises
    123         FdtException if val < 0
    124     """
    125     if val < 0:
    126         if -val not in quiet:
    127             raise FdtException(val)
    128     return val
    129 
    130 def check_err_null(val, quiet=()):
    131     """Raise an error if the return value is NULL
    132 
    133     This is used to check for a NULL return value from certain libfdt C
    134     functions
    135 
    136     Args:
    137         val: Return value from a libfdt function
    138         quiet: Errors to ignore (empty to raise on all errors)
    139 
    140     Returns:
    141         val if val is a list, None if not
    142 
    143     Raises
    144         FdtException if val indicates an error was reported and the error
    145         is not in @quiet.
    146     """
    147     # Normally a list is returned which contains the data and its length.
    148     # If we get just an integer error code, it means the function failed.
    149     if not isinstance(val, list):
    150         if -val not in quiet:
    151             raise FdtException(val)
    152     return val
    153 
    154 class Fdt:
    155     """Device tree class, supporting all operations
    156 
    157     The Fdt object is created is created from a device tree binary file,
    158     e.g. with something like:
    159 
    160        fdt = Fdt(open("filename.dtb").read())
    161 
    162     Operations can then be performed using the methods in this class. Each
    163     method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
    164 
    165     All methods raise an FdtException if an error occurs. To avoid this
    166     behaviour a 'quiet' parameter is provided for some functions. This
    167     defaults to empty, but you can pass a list of errors that you expect.
    168     If one of these errors occurs, the function will return an error number
    169     (e.g. -NOTFOUND).
    170     """
    171     def __init__(self, data):
    172         self._fdt = bytearray(data)
    173         check_err(fdt_check_header(self._fdt));
    174 
    175     def path_offset(self, path, quiet=()):
    176         """Get the offset for a given path
    177 
    178         Args:
    179             path: Path to the required node, e.g. '/node@3/subnode@1'
    180             quiet: Errors to ignore (empty to raise on all errors)
    181 
    182         Returns:
    183             Node offset
    184 
    185         Raises
    186             FdtException if the path is not valid or not found
    187         """
    188         return check_err(fdt_path_offset(self._fdt, path), quiet)
    189 
    190     def first_property_offset(self, nodeoffset, quiet=()):
    191         """Get the offset of the first property in a node offset
    192 
    193         Args:
    194             nodeoffset: Offset to the node to check
    195             quiet: Errors to ignore (empty to raise on all errors)
    196 
    197         Returns:
    198             Offset of the first property
    199 
    200         Raises
    201             FdtException if the associated node has no properties, or some
    202                 other error occurred
    203         """
    204         return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
    205                          quiet)
    206 
    207     def next_property_offset(self, prop_offset, quiet=()):
    208         """Get the next property in a node
    209 
    210         Args:
    211             prop_offset: Offset of the previous property
    212             quiet: Errors to ignore (empty to raise on all errors)
    213 
    214         Returns:
    215             Offset of the next property
    216 
    217         Raises:
    218             FdtException if the associated node has no more properties, or
    219                 some other error occurred
    220         """
    221         return check_err(fdt_next_property_offset(self._fdt, prop_offset),
    222                          quiet)
    223 
    224     def get_name(self, nodeoffset):
    225         """Get the name of a node
    226 
    227         Args:
    228             nodeoffset: Offset of node to check
    229 
    230         Returns:
    231             Node name
    232 
    233         Raises:
    234             FdtException on error (e.g. nodeoffset is invalid)
    235         """
    236         return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
    237 
    238     def get_property_by_offset(self, prop_offset, quiet=()):
    239         """Obtains a property that can be examined
    240 
    241         Args:
    242             prop_offset: Offset of property (e.g. from first_property_offset())
    243             quiet: Errors to ignore (empty to raise on all errors)
    244 
    245         Returns:
    246             Property object, or None if not found
    247 
    248         Raises:
    249             FdtException on error (e.g. invalid prop_offset or device
    250             tree format)
    251         """
    252         pdata = check_err_null(
    253                 fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
    254         if isinstance(pdata, (int)):
    255             return pdata
    256         return Property(pdata[0], pdata[1])
    257 
    258     def first_subnode(self, nodeoffset, quiet=()):
    259         """Find the first subnode of a parent node
    260 
    261         Args:
    262             nodeoffset: Node offset of parent node
    263             quiet: Errors to ignore (empty to raise on all errors)
    264 
    265         Returns:
    266             The offset of the first subnode, if any
    267 
    268         Raises:
    269             FdtException if no subnode found or other error occurs
    270         """
    271         return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
    272 
    273     def next_subnode(self, nodeoffset, quiet=()):
    274         """Find the next subnode
    275 
    276         Args:
    277             nodeoffset: Node offset of previous subnode
    278             quiet: Errors to ignore (empty to raise on all errors)
    279 
    280         Returns:
    281             The offset of the next subnode, if any
    282 
    283         Raises:
    284             FdtException if no more subnode found or other error occurs
    285         """
    286         return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
    287 
    288     def totalsize(self):
    289         """Return the total size of the device tree
    290 
    291         Returns:
    292             Total tree size in bytes
    293         """
    294         return check_err(fdt_totalsize(self._fdt))
    295 
    296     def off_dt_struct(self):
    297         """Return the start of the device tree struct area
    298 
    299         Returns:
    300             Start offset of struct area
    301         """
    302         return check_err(fdt_off_dt_struct(self._fdt))
    303 
    304     def pack(self, quiet=()):
    305         """Pack the device tree to remove unused space
    306 
    307         This adjusts the tree in place.
    308 
    309         Args:
    310             quiet: Errors to ignore (empty to raise on all errors)
    311 
    312         Raises:
    313             FdtException if any error occurs
    314         """
    315         return check_err(fdt_pack(self._fdt), quiet)
    316 
    317     def delprop(self, nodeoffset, prop_name):
    318         """Delete a property from a node
    319 
    320         Args:
    321             nodeoffset: Node offset containing property to delete
    322             prop_name: Name of property to delete
    323 
    324         Raises:
    325             FdtError if the property does not exist, or another error occurs
    326         """
    327         return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
    328 
    329     def getprop(self, nodeoffset, prop_name, quiet=()):
    330         """Get a property from a node
    331 
    332         Args:
    333             nodeoffset: Node offset containing property to get
    334             prop_name: Name of property to get
    335             quiet: Errors to ignore (empty to raise on all errors)
    336 
    337         Returns:
    338             Value of property as a bytearray, or -ve error number
    339 
    340         Raises:
    341             FdtError if any error occurs (e.g. the property is not found)
    342         """
    343         pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
    344                                quiet)
    345         if isinstance(pdata, (int)):
    346             return pdata
    347         return bytearray(pdata[0])
    348 
    349 
    350 class Property:
    351     """Holds a device tree property name and value.
    352 
    353     This holds a copy of a property taken from the device tree. It does not
    354     reference the device tree, so if anything changes in the device tree,
    355     a Property object will remain valid.
    356 
    357     Properties:
    358         name: Property name
    359         value: Proper value as a bytearray
    360     """
    361     def __init__(self, name, value):
    362         self.name = name
    363         self.value = value
    364 %}
    365 
    366 %rename(fdt_property) fdt_property_func;
    367 
    368 typedef int fdt32_t;
    369 
    370 %include "libfdt/fdt.h"
    371 
    372 %include "typemaps.i"
    373 
    374 /* Most functions don't change the device tree, so use a const void * */
    375 %typemap(in) (const void *)(const void *fdt) {
    376 	if (!PyByteArray_Check($input)) {
    377 		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
    378 			"', argument " "$argnum"" of type '" "$type""'");
    379 	}
    380 	$1 = (void *)PyByteArray_AsString($input);
    381         fdt = $1;
    382         fdt = fdt; /* avoid unused variable warning */
    383 }
    384 
    385 /* Some functions do change the device tree, so use void * */
    386 %typemap(in) (void *)(const void *fdt) {
    387 	if (!PyByteArray_Check($input)) {
    388 		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
    389 			"', argument " "$argnum"" of type '" "$type""'");
    390 	}
    391 	$1 = PyByteArray_AsString($input);
    392         fdt = $1;
    393         fdt = fdt; /* avoid unused variable warning */
    394 }
    395 
    396 %typemap(out) (struct fdt_property *) {
    397 	PyObject *buff;
    398 
    399 	if ($1) {
    400 		resultobj = PyString_FromString(
    401 			fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
    402 		buff = PyByteArray_FromStringAndSize(
    403 			(const char *)($1 + 1), fdt32_to_cpu($1->len));
    404 		resultobj = SWIG_Python_AppendOutput(resultobj, buff);
    405 	}
    406 }
    407 
    408 %apply int *OUTPUT { int *lenp };
    409 
    410 /* typemap used for fdt_getprop() */
    411 %typemap(out) (const void *) {
    412 	if (!$1)
    413 		$result = Py_None;
    414 	else
    415 		$result = Py_BuildValue("s#", $1, *arg4);
    416 }
    417 
    418 /* We have both struct fdt_property and a function fdt_property() */
    419 %warnfilter(302) fdt_property;
    420 
    421 /* These are macros in the header so have to be redefined here */
    422 int fdt_magic(const void *fdt);
    423 int fdt_totalsize(const void *fdt);
    424 int fdt_off_dt_struct(const void *fdt);
    425 int fdt_off_dt_strings(const void *fdt);
    426 int fdt_off_mem_rsvmap(const void *fdt);
    427 int fdt_version(const void *fdt);
    428 int fdt_last_comp_version(const void *fdt);
    429 int fdt_boot_cpuid_phys(const void *fdt);
    430 int fdt_size_dt_strings(const void *fdt);
    431 int fdt_size_dt_struct(const void *fdt);
    432 
    433 %include <../libfdt/libfdt.h>
    434