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