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