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