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, indir=None):
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        indir: List of directories where input files can be found
67
68    Returns:
69        Filename of resulting .dtb file
70    """
71    _, ext = os.path.splitext(fname)
72    if ext != '.dts':
73        return fname
74
75    if tmpdir:
76        dts_input = os.path.join(tmpdir, 'source.dts')
77        dtb_output = os.path.join(tmpdir, 'source.dtb')
78    else:
79        dts_input = tools.get_output_filename('source.dts')
80        dtb_output = tools.get_output_filename('source.dtb')
81
82    search_paths = [os.path.join(os.getcwd(), 'include')]
83    if indir is not None:
84        search_paths += indir
85    root, _ = os.path.splitext(fname)
86    cc, args = tools.get_target_compile_tool('cc')
87    args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__']
88    args += ['-Ulinux']
89    for path in search_paths:
90        args.extend(['-I', path])
91    args += ['-o', dts_input, fname]
92    command.run(cc, *args)
93
94    # If we don't have a directory, put it in the tools tempdir
95    search_list = []
96    for path in search_paths:
97        search_list.extend(['-i', path])
98    dtc, args = tools.get_target_compile_tool('dtc')
99    args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb',
100            '-W', 'no-unit_address_vs_reg']
101    args.extend(search_list)
102    args.append(dts_input)
103    command.run(dtc, *args, capture_stderr=capture_stderr)
104    return dtb_output
105
106def GetInt(node, propname, default=None):
107    """Get an integer from a property
108
109    Args:
110        node: Node object to read from
111        propname: property name to read
112        default: Default value to use if the node/property do not exist
113
114    Returns:
115        Integer value read, or default if none
116    """
117    prop = node.props.get(propname)
118    if not prop:
119        return default
120    if isinstance(prop.value, list):
121        raise ValueError("Node '%s' property '%s' has list value: expecting "
122                         "a single integer" % (node.name, propname))
123    value = fdt32_to_cpu(prop.value)
124    return value
125
126def GetInt64(node, propname, default=None):
127    """Get a 64-bit integer from a property
128
129    Args:
130        node (Node): Node object to read from
131        propname (str): property name to read
132        default (int): Default value to use if the node/property do not exist
133
134    Returns:
135        int: value read, or default if none
136
137    Raises:
138        ValueError: Property is not of the correct size
139    """
140    prop = node.props.get(propname)
141    if not prop:
142        return default
143    if not isinstance(prop.value, list) or len(prop.value) != 2:
144        raise ValueError("Node '%s' property '%s' should be a list with 2 items for 64-bit values" %
145                         (node.name, propname))
146    value = fdt64_to_cpu(prop.value)
147    return value
148
149def GetString(node, propname, default=None):
150    """Get a string from a property
151
152    Args:
153        node: Node object to read from
154        propname: property name to read
155        default: Default value to use if the node/property do not exist
156
157    Returns:
158        String value read, or default if none
159    """
160    prop = node.props.get(propname)
161    if not prop:
162        return default
163    value = prop.value
164    if not prop.bytes:
165        return ''
166    if isinstance(value, list):
167        raise ValueError("Node '%s' property '%s' has list value: expecting "
168                         "a single string" % (node.name, propname))
169    return value
170
171def GetStringList(node, propname, default=None):
172    """Get a string list from a property
173
174    Args:
175        node (Node): Node object to read from
176        propname (str): property name to read
177        default (list of str): Default value to use if the node/property do not
178            exist, or None
179
180    Returns:
181        String value read, or default if none
182    """
183    prop = node.props.get(propname)
184    if not prop:
185        return default
186    value = prop.value
187    if not prop.bytes:
188        return []
189    if not isinstance(value, list):
190        strval = GetString(node, propname)
191        return [strval]
192    return value
193
194def GetArgs(node, propname):
195    prop = node.props.get(propname)
196    if not prop:
197        raise ValueError(f"Node '{node.path}': Expected property '{propname}'")
198    if prop.bytes:
199        value = GetStringList(node, propname)
200    else:
201        value = []
202    if not value:
203        args = []
204    elif len(value) == 1:
205        args = value[0].split()
206    else:
207        args = value
208    return args
209
210def GetBool(node, propname, default=False):
211    """Get an boolean from a property
212
213    Args:
214        node: Node object to read from
215        propname: property name to read
216        default: Default value to use if the node/property do not exist
217
218    Returns:
219        Boolean value read, or default if none (if you set this to True the
220            function will always return True)
221    """
222    if propname in node.props:
223        return True
224    return default
225
226def GetByte(node, propname, default=None):
227    """Get an byte from a property
228
229    Args:
230        node: Node object to read from
231        propname: property name to read
232        default: Default value to use if the node/property do not exist
233
234    Returns:
235        Byte value read, or default if none
236    """
237    prop = node.props.get(propname)
238    if not prop:
239        return default
240    value = prop.value
241    if isinstance(value, list):
242        raise ValueError("Node '%s' property '%s' has list value: expecting "
243                         "a single byte" % (node.name, propname))
244    if len(value) != 1:
245        raise ValueError("Node '%s' property '%s' has length %d, expecting %d" %
246                         (node.name, propname, len(value), 1))
247    return ord(value[0])
248
249def GetBytes(node, propname, size, default=None):
250    """Get a set of bytes from a property
251
252    Args:
253        node (Node): Node object to read from
254        propname (str): property name to read
255        size (int): Number of bytes to expect
256        default (bytes): Default value or None
257
258    Returns:
259        bytes: Bytes value read, or default if none
260    """
261    prop = node.props.get(propname)
262    if not prop:
263        return default
264    if len(prop.bytes) != size:
265        raise ValueError("Node '%s' property '%s' has length %d, expecting %d" %
266                         (node.name, propname, len(prop.bytes), size))
267    return prop.bytes
268
269def GetPhandleList(node, propname):
270    """Get a list of phandles from a property
271
272    Args:
273        node: Node object to read from
274        propname: property name to read
275
276    Returns:
277        List of phandles read, each an integer
278    """
279    prop = node.props.get(propname)
280    if not prop:
281        return None
282    value = prop.value
283    if not isinstance(value, list):
284        value = [value]
285    return [fdt32_to_cpu(v) for v in value]
286
287def GetPhandleNameOffset(node, propname):
288    """Get a <&phandle>, "string", <offset> value from a property
289
290    Args:
291        node: Node object to read from
292        propname: property name to read
293
294    Returns:
295        tuple:
296            Node object
297            str
298            int
299        or None if the property does not exist
300    """
301    prop = node.props.get(propname)
302    if not prop:
303        return None
304    value = prop.bytes
305    phandle = fdt32_to_cpu(value[:4])
306    node = node.GetFdt().LookupPhandle(phandle)
307    name = ''
308    for byte in value[4:]:
309        if not byte:
310            break
311        name += chr(byte)
312    val = fdt32_to_cpu(value[4 + len(name) + 1:])
313    return node, name, val
314
315def GetDatatype(node, propname, datatype):
316    """Get a value of a given type from a property
317
318    Args:
319        node: Node object to read from
320        propname: property name to read
321        datatype: Type to read (str or int)
322
323    Returns:
324        value read, or None if none
325
326    Raises:
327        ValueError if datatype is not str or int
328    """
329    if datatype == str:
330        return GetString(node, propname)
331    elif datatype == int:
332        return GetInt(node, propname)
333    raise ValueError("fdt_util internal error: Unknown data type '%s'" %
334                     datatype)
335