• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * pylibfdt - Flat Device Tree manipulation in Python
3  * Copyright (C) 2017 Google, Inc.
4  * Written by Simon Glass <sjg@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