1#!/usr/bin/python 2# SPDX-License-Identifier: GPL-2.0+ 3# 4# Copyright (C) 2016 Google, Inc 5# Written by Simon Glass <sjg@chromium.org> 6# 7 8# Utility functions for reading from a device tree. Once the upstream pylibfdt 9# implementation advances far enough, we should be able to drop these. 10 11import os 12import struct 13import sys 14import tempfile 15 16from u_boot_pylib import command 17from u_boot_pylib import tools 18 19def fdt32_to_cpu(val): 20 """Convert a device tree cell to an integer 21 22 Args: 23 Value to convert (4-character string representing the cell value) 24 25 Return: 26 A native-endian integer value 27 """ 28 return struct.unpack('>I', val)[0] 29 30def fdt64_to_cpu(val): 31 """Convert a device tree cell to an integer 32 33 Args: 34 val (list): Value to convert (list of 2 4-character strings representing 35 the cell value) 36 37 Return: 38 int: A native-endian integer value 39 """ 40 return fdt32_to_cpu(val[0]) << 32 | fdt32_to_cpu(val[1]) 41 42def fdt_cells_to_cpu(val, cells): 43 """Convert one or two cells to a long integer 44 45 Args: 46 Value to convert (array of one or more 4-character strings) 47 48 Return: 49 A native-endian integer value 50 """ 51 if not cells: 52 return 0 53 out = int(fdt32_to_cpu(val[0])) 54 if cells == 2: 55 out = out << 32 | fdt32_to_cpu(val[1]) 56 return out 57 58def EnsureCompiled(fname, tmpdir=None, capture_stderr=False): 59 """Compile an fdt .dts source file into a .dtb binary blob if needed. 60 61 Args: 62 fname: Filename (if .dts it will be compiled). It not it will be 63 left alone 64 tmpdir: Temporary directory for output files, or None to use the 65 tools-module output directory 66 67 Returns: 68 Filename of resulting .dtb file 69 """ 70 _, ext = os.path.splitext(fname) 71 if ext != '.dts': 72 return fname 73 74 if tmpdir: 75 dts_input = os.path.join(tmpdir, 'source.dts') 76 dtb_output = os.path.join(tmpdir, 'source.dtb') 77 else: 78 dts_input = tools.get_output_filename('source.dts') 79 dtb_output = tools.get_output_filename('source.dtb') 80 81 search_paths = [os.path.join(os.getcwd(), 'include')] 82 root, _ = os.path.splitext(fname) 83 cc, args = tools.get_target_compile_tool('cc') 84 args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__'] 85 args += ['-Ulinux'] 86 for path in search_paths: 87 args.extend(['-I', path]) 88 args += ['-o', dts_input, fname] 89 command.run(cc, *args) 90 91 # If we don't have a directory, put it in the tools tempdir 92 search_list = [] 93 for path in search_paths: 94 search_list.extend(['-i', path]) 95 dtc, args = tools.get_target_compile_tool('dtc') 96 args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb', 97 '-W', 'no-unit_address_vs_reg'] 98 args.extend(search_list) 99 args.append(dts_input) 100 command.run(dtc, *args, capture_stderr=capture_stderr) 101 return dtb_output 102 103def GetInt(node, propname, default=None): 104 """Get an integer from a property 105 106 Args: 107 node: Node object to read from 108 propname: property name to read 109 default: Default value to use if the node/property do not exist 110 111 Returns: 112 Integer value read, or default if none 113 """ 114 prop = node.props.get(propname) 115 if not prop: 116 return default 117 if isinstance(prop.value, list): 118 raise ValueError("Node '%s' property '%s' has list value: expecting " 119 "a single integer" % (node.name, propname)) 120 value = fdt32_to_cpu(prop.value) 121 return value 122 123def GetInt64(node, propname, default=None): 124 """Get a 64-bit integer from a property 125 126 Args: 127 node (Node): Node object to read from 128 propname (str): property name to read 129 default (int): Default value to use if the node/property do not exist 130 131 Returns: 132 int: value read, or default if none 133 134 Raises: 135 ValueError: Property is not of the correct size 136 """ 137 prop = node.props.get(propname) 138 if not prop: 139 return default 140 if not isinstance(prop.value, list) or len(prop.value) != 2: 141 raise ValueError("Node '%s' property '%s' should be a list with 2 items for 64-bit values" % 142 (node.name, propname)) 143 value = fdt64_to_cpu(prop.value) 144 return value 145 146def GetString(node, propname, default=None): 147 """Get a string from a property 148 149 Args: 150 node: Node object to read from 151 propname: property name to read 152 default: Default value to use if the node/property do not exist 153 154 Returns: 155 String value read, or default if none 156 """ 157 prop = node.props.get(propname) 158 if not prop: 159 return default 160 value = prop.value 161 if not prop.bytes: 162 return '' 163 if isinstance(value, list): 164 raise ValueError("Node '%s' property '%s' has list value: expecting " 165 "a single string" % (node.name, propname)) 166 return value 167 168def GetStringList(node, propname, default=None): 169 """Get a string list from a property 170 171 Args: 172 node (Node): Node object to read from 173 propname (str): property name to read 174 default (list of str): Default value to use if the node/property do not 175 exist, or None 176 177 Returns: 178 String value read, or default if none 179 """ 180 prop = node.props.get(propname) 181 if not prop: 182 return default 183 value = prop.value 184 if not prop.bytes: 185 return [] 186 if not isinstance(value, list): 187 strval = GetString(node, propname) 188 return [strval] 189 return value 190 191def GetArgs(node, propname): 192 prop = node.props.get(propname) 193 if not prop: 194 raise ValueError(f"Node '{node.path}': Expected property '{propname}'") 195 if prop.bytes: 196 value = GetStringList(node, propname) 197 else: 198 value = [] 199 if not value: 200 args = [] 201 elif len(value) == 1: 202 args = value[0].split() 203 else: 204 args = value 205 return args 206 207def GetBool(node, propname, default=False): 208 """Get an boolean from a property 209 210 Args: 211 node: Node object to read from 212 propname: property name to read 213 default: Default value to use if the node/property do not exist 214 215 Returns: 216 Boolean value read, or default if none (if you set this to True the 217 function will always return True) 218 """ 219 if propname in node.props: 220 return True 221 return default 222 223def GetByte(node, propname, default=None): 224 """Get an byte from a property 225 226 Args: 227 node: Node object to read from 228 propname: property name to read 229 default: Default value to use if the node/property do not exist 230 231 Returns: 232 Byte value read, or default if none 233 """ 234 prop = node.props.get(propname) 235 if not prop: 236 return default 237 value = prop.value 238 if isinstance(value, list): 239 raise ValueError("Node '%s' property '%s' has list value: expecting " 240 "a single byte" % (node.name, propname)) 241 if len(value) != 1: 242 raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % 243 (node.name, propname, len(value), 1)) 244 return ord(value[0]) 245 246def GetBytes(node, propname, size, default=None): 247 """Get a set of bytes from a property 248 249 Args: 250 node (Node): Node object to read from 251 propname (str): property name to read 252 size (int): Number of bytes to expect 253 default (bytes): Default value or None 254 255 Returns: 256 bytes: Bytes value read, or default if none 257 """ 258 prop = node.props.get(propname) 259 if not prop: 260 return default 261 if len(prop.bytes) != size: 262 raise ValueError("Node '%s' property '%s' has length %d, expecting %d" % 263 (node.name, propname, len(prop.bytes), size)) 264 return prop.bytes 265 266def GetPhandleList(node, propname): 267 """Get a list of phandles from a property 268 269 Args: 270 node: Node object to read from 271 propname: property name to read 272 273 Returns: 274 List of phandles read, each an integer 275 """ 276 prop = node.props.get(propname) 277 if not prop: 278 return None 279 value = prop.value 280 if not isinstance(value, list): 281 value = [value] 282 return [fdt32_to_cpu(v) for v in value] 283 284def GetPhandleNameOffset(node, propname): 285 """Get a <&phandle>, "string", <offset> value from a property 286 287 Args: 288 node: Node object to read from 289 propname: property name to read 290 291 Returns: 292 tuple: 293 Node object 294 str 295 int 296 or None if the property does not exist 297 """ 298 prop = node.props.get(propname) 299 if not prop: 300 return None 301 value = prop.bytes 302 phandle = fdt32_to_cpu(value[:4]) 303 node = node.GetFdt().LookupPhandle(phandle) 304 name = '' 305 for byte in value[4:]: 306 if not byte: 307 break 308 name += chr(byte) 309 val = fdt32_to_cpu(value[4 + len(name) + 1:]) 310 return node, name, val 311 312def GetDatatype(node, propname, datatype): 313 """Get a value of a given type from a property 314 315 Args: 316 node: Node object to read from 317 propname: property name to read 318 datatype: Type to read (str or int) 319 320 Returns: 321 value read, or None if none 322 323 Raises: 324 ValueError if datatype is not str or int 325 """ 326 if datatype == str: 327 return GetString(node, propname) 328 elif datatype == int: 329 return GetInt(node, propname) 330 raise ValueError("fdt_util internal error: Unknown data type '%s'" % 331 datatype) 332