2016-07-04 17:58:08 +00:00
|
|
|
#!/usr/bin/python
|
2018-05-06 21:58:06 +00:00
|
|
|
# SPDX-License-Identifier: GPL-2.0+
|
2016-07-04 17:58:08 +00:00
|
|
|
#
|
|
|
|
# Copyright (C) 2016 Google, Inc
|
|
|
|
# Written by Simon Glass <sjg@chromium.org>
|
|
|
|
#
|
|
|
|
|
2018-07-17 19:25:31 +00:00
|
|
|
# Utility functions for reading from a device tree. Once the upstream pylibfdt
|
|
|
|
# implementation advances far enough, we should be able to drop these.
|
|
|
|
|
2016-07-26 00:59:10 +00:00
|
|
|
import os
|
2016-07-04 17:58:08 +00:00
|
|
|
import struct
|
2016-09-27 15:03:57 +00:00
|
|
|
import sys
|
2016-07-26 00:59:10 +00:00
|
|
|
import tempfile
|
|
|
|
|
2020-04-18 00:09:04 +00:00
|
|
|
from patman import command
|
|
|
|
from patman import tools
|
2016-07-04 17:58:08 +00:00
|
|
|
|
|
|
|
def fdt32_to_cpu(val):
|
|
|
|
"""Convert a device tree cell to an integer
|
|
|
|
|
|
|
|
Args:
|
|
|
|
Value to convert (4-character string representing the cell value)
|
|
|
|
|
|
|
|
Return:
|
|
|
|
A native-endian integer value
|
|
|
|
"""
|
2016-07-26 00:59:17 +00:00
|
|
|
return struct.unpack('>I', val)[0]
|
2016-07-26 00:59:10 +00:00
|
|
|
|
2021-11-23 18:03:39 +00:00
|
|
|
def fdt64_to_cpu(val):
|
|
|
|
"""Convert a device tree cell to an integer
|
|
|
|
|
|
|
|
Args:
|
|
|
|
val (list): Value to convert (list of 2 4-character strings representing
|
|
|
|
the cell value)
|
|
|
|
|
|
|
|
Return:
|
|
|
|
int: A native-endian integer value
|
|
|
|
"""
|
|
|
|
return fdt32_to_cpu(val[0]) << 32 | fdt32_to_cpu(val[1])
|
|
|
|
|
2017-08-29 20:15:48 +00:00
|
|
|
def fdt_cells_to_cpu(val, cells):
|
|
|
|
"""Convert one or two cells to a long integer
|
|
|
|
|
|
|
|
Args:
|
|
|
|
Value to convert (array of one or more 4-character strings)
|
|
|
|
|
|
|
|
Return:
|
2019-05-18 04:00:42 +00:00
|
|
|
A native-endian integer value
|
2017-08-29 20:15:48 +00:00
|
|
|
"""
|
2017-08-29 20:15:50 +00:00
|
|
|
if not cells:
|
|
|
|
return 0
|
2019-05-18 04:00:42 +00:00
|
|
|
out = int(fdt32_to_cpu(val[0]))
|
2017-08-29 20:15:48 +00:00
|
|
|
if cells == 2:
|
|
|
|
out = out << 32 | fdt32_to_cpu(val[1])
|
|
|
|
return out
|
|
|
|
|
2019-07-20 18:23:49 +00:00
|
|
|
def EnsureCompiled(fname, tmpdir=None, capture_stderr=False):
|
2016-07-26 00:59:10 +00:00
|
|
|
"""Compile an fdt .dts source file into a .dtb binary blob if needed.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
fname: Filename (if .dts it will be compiled). It not it will be
|
|
|
|
left alone
|
2019-07-20 18:23:49 +00:00
|
|
|
tmpdir: Temporary directory for output files, or None to use the
|
|
|
|
tools-module output directory
|
2016-07-26 00:59:10 +00:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
Filename of resulting .dtb file
|
|
|
|
"""
|
|
|
|
_, ext = os.path.splitext(fname)
|
|
|
|
if ext != '.dts':
|
|
|
|
return fname
|
|
|
|
|
2019-07-20 18:23:49 +00:00
|
|
|
if tmpdir:
|
|
|
|
dts_input = os.path.join(tmpdir, 'source.dts')
|
|
|
|
dtb_output = os.path.join(tmpdir, 'source.dtb')
|
|
|
|
else:
|
2022-01-29 21:14:04 +00:00
|
|
|
dts_input = tools.get_output_filename('source.dts')
|
|
|
|
dtb_output = tools.get_output_filename('source.dtb')
|
2016-07-26 00:59:10 +00:00
|
|
|
|
|
|
|
search_paths = [os.path.join(os.getcwd(), 'include')]
|
|
|
|
root, _ = os.path.splitext(fname)
|
2022-01-29 21:14:04 +00:00
|
|
|
cc, args = tools.get_target_compile_tool('cc')
|
binman: Use target-specific tools when cross-compiling
Currently, binman always runs the compile tools like cc, objcopy, strip,
etc. using their literal name. Instead, this patch makes it use the
target-specific versions by default, derived from the tool-specific
environment variables (CC, OBJCOPY, STRIP, etc.) or from the
CROSS_COMPILE environment variable.
For example, the u-boot-elf etype directly uses 'strip'. Trying to run
the tests with 'CROSS_COMPILE=i686-linux-gnu- binman test' on an arm64
host results in the '097_elf_strip.dts' test to fail as the arm64
version of 'strip' can't understand the format of the x86 ELF file.
This also adjusts some command.Output() calls that caused test errors or
failures to use the target versions of the tools they call. After this,
patch, an arm64 host can run all tests with no errors or failures using
a correct CROSS_COMPILE value.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
2020-09-06 11:46:05 +00:00
|
|
|
args += ['-E', '-P', '-x', 'assembler-with-cpp', '-D__ASSEMBLY__']
|
2016-07-26 00:59:10 +00:00
|
|
|
args += ['-Ulinux']
|
|
|
|
for path in search_paths:
|
|
|
|
args.extend(['-I', path])
|
|
|
|
args += ['-o', dts_input, fname]
|
2022-01-29 21:14:05 +00:00
|
|
|
command.run(cc, *args)
|
2016-07-26 00:59:10 +00:00
|
|
|
|
|
|
|
# If we don't have a directory, put it in the tools tempdir
|
|
|
|
search_list = []
|
|
|
|
for path in search_paths:
|
|
|
|
search_list.extend(['-i', path])
|
2022-01-29 21:14:04 +00:00
|
|
|
dtc, args = tools.get_target_compile_tool('dtc')
|
binman: Use target-specific tools when cross-compiling
Currently, binman always runs the compile tools like cc, objcopy, strip,
etc. using their literal name. Instead, this patch makes it use the
target-specific versions by default, derived from the tool-specific
environment variables (CC, OBJCOPY, STRIP, etc.) or from the
CROSS_COMPILE environment variable.
For example, the u-boot-elf etype directly uses 'strip'. Trying to run
the tests with 'CROSS_COMPILE=i686-linux-gnu- binman test' on an arm64
host results in the '097_elf_strip.dts' test to fail as the arm64
version of 'strip' can't understand the format of the x86 ELF file.
This also adjusts some command.Output() calls that caused test errors or
failures to use the target versions of the tools they call. After this,
patch, an arm64 host can run all tests with no errors or failures using
a correct CROSS_COMPILE value.
Signed-off-by: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
2020-09-06 11:46:05 +00:00
|
|
|
args += ['-I', 'dts', '-o', dtb_output, '-O', 'dtb',
|
2017-11-13 04:52:09 +00:00
|
|
|
'-W', 'no-unit_address_vs_reg']
|
2016-07-26 00:59:10 +00:00
|
|
|
args.extend(search_list)
|
|
|
|
args.append(dts_input)
|
2022-01-29 21:14:05 +00:00
|
|
|
command.run(dtc, *args, capture_stderr=capture_stderr)
|
2016-07-26 00:59:10 +00:00
|
|
|
return dtb_output
|
2016-07-26 00:59:18 +00:00
|
|
|
|
|
|
|
def GetInt(node, propname, default=None):
|
2018-07-17 19:25:31 +00:00
|
|
|
"""Get an integer from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node: Node object to read from
|
|
|
|
propname: property name to read
|
|
|
|
default: Default value to use if the node/property do not exist
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Integer value read, or default if none
|
|
|
|
"""
|
2016-07-26 00:59:18 +00:00
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return default
|
2018-07-06 16:27:28 +00:00
|
|
|
if isinstance(prop.value, list):
|
|
|
|
raise ValueError("Node '%s' property '%s' has list value: expecting "
|
2016-07-26 00:59:18 +00:00
|
|
|
"a single integer" % (node.name, propname))
|
2018-07-06 16:27:28 +00:00
|
|
|
value = fdt32_to_cpu(prop.value)
|
2016-07-26 00:59:18 +00:00
|
|
|
return value
|
|
|
|
|
2021-11-23 18:03:39 +00:00
|
|
|
def GetInt64(node, propname, default=None):
|
|
|
|
"""Get a 64-bit integer from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node (Node): Node object to read from
|
|
|
|
propname (str): property name to read
|
|
|
|
default (int): Default value to use if the node/property do not exist
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int: value read, or default if none
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
ValueError: Property is not of the correct size
|
|
|
|
"""
|
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return default
|
|
|
|
if not isinstance(prop.value, list) or len(prop.value) != 2:
|
|
|
|
raise ValueError("Node '%s' property '%s' should be a list with 2 items for 64-bit values" %
|
|
|
|
(node.name, propname))
|
|
|
|
value = fdt64_to_cpu(prop.value)
|
|
|
|
return value
|
|
|
|
|
2016-07-26 00:59:18 +00:00
|
|
|
def GetString(node, propname, default=None):
|
2018-07-17 19:25:31 +00:00
|
|
|
"""Get a string from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node: Node object to read from
|
|
|
|
propname: property name to read
|
|
|
|
default: Default value to use if the node/property do not exist
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
String value read, or default if none
|
|
|
|
"""
|
2016-07-26 00:59:18 +00:00
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return default
|
|
|
|
value = prop.value
|
2018-07-06 16:27:28 +00:00
|
|
|
if isinstance(value, list):
|
|
|
|
raise ValueError("Node '%s' property '%s' has list value: expecting "
|
2016-07-26 00:59:18 +00:00
|
|
|
"a single string" % (node.name, propname))
|
|
|
|
return value
|
|
|
|
|
2021-11-24 04:09:51 +00:00
|
|
|
def GetStringList(node, propname, default=None):
|
|
|
|
"""Get a string list from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node (Node): Node object to read from
|
|
|
|
propname (str): property name to read
|
|
|
|
default (list of str): Default value to use if the node/property do not
|
|
|
|
exist, or None
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
String value read, or default if none
|
|
|
|
"""
|
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return default
|
|
|
|
value = prop.value
|
|
|
|
if not isinstance(value, list):
|
|
|
|
strval = GetString(node, propname)
|
|
|
|
return [strval]
|
|
|
|
return value
|
|
|
|
|
2022-02-08 18:49:53 +00:00
|
|
|
def GetArgs(node, propname):
|
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
raise ValueError(f"Node '{node.path}': Expected property '{propname}'")
|
|
|
|
if prop.bytes:
|
|
|
|
value = GetStringList(node, propname)
|
|
|
|
else:
|
|
|
|
value = []
|
|
|
|
lists = [v.split() for v in value]
|
|
|
|
args = [x for l in lists for x in l]
|
|
|
|
return args
|
|
|
|
|
2016-07-26 00:59:18 +00:00
|
|
|
def GetBool(node, propname, default=False):
|
2018-07-17 19:25:31 +00:00
|
|
|
"""Get an boolean from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node: Node object to read from
|
|
|
|
propname: property name to read
|
|
|
|
default: Default value to use if the node/property do not exist
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Boolean value read, or default if none (if you set this to True the
|
|
|
|
function will always return True)
|
|
|
|
"""
|
2016-07-26 00:59:18 +00:00
|
|
|
if propname in node.props:
|
|
|
|
return True
|
|
|
|
return default
|
2018-07-17 19:25:32 +00:00
|
|
|
|
2018-07-17 19:25:40 +00:00
|
|
|
def GetByte(node, propname, default=None):
|
|
|
|
"""Get an byte from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node: Node object to read from
|
|
|
|
propname: property name to read
|
|
|
|
default: Default value to use if the node/property do not exist
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Byte value read, or default if none
|
|
|
|
"""
|
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return default
|
|
|
|
value = prop.value
|
|
|
|
if isinstance(value, list):
|
|
|
|
raise ValueError("Node '%s' property '%s' has list value: expecting "
|
|
|
|
"a single byte" % (node.name, propname))
|
|
|
|
if len(value) != 1:
|
|
|
|
raise ValueError("Node '%s' property '%s' has length %d, expecting %d" %
|
|
|
|
(node.name, propname, len(value), 1))
|
|
|
|
return ord(value[0])
|
|
|
|
|
2021-11-23 18:03:40 +00:00
|
|
|
def GetBytes(node, propname, size, default=None):
|
|
|
|
"""Get a set of bytes from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node (Node): Node object to read from
|
|
|
|
propname (str): property name to read
|
|
|
|
size (int): Number of bytes to expect
|
|
|
|
default (bytes): Default value or None
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bytes: Bytes value read, or default if none
|
|
|
|
"""
|
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return default
|
|
|
|
if len(prop.bytes) != size:
|
|
|
|
raise ValueError("Node '%s' property '%s' has length %d, expecting %d" %
|
|
|
|
(node.name, propname, len(prop.bytes), size))
|
|
|
|
return prop.bytes
|
|
|
|
|
2018-07-17 19:25:46 +00:00
|
|
|
def GetPhandleList(node, propname):
|
|
|
|
"""Get a list of phandles from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node: Node object to read from
|
|
|
|
propname: property name to read
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
List of phandles read, each an integer
|
|
|
|
"""
|
|
|
|
prop = node.props.get(propname)
|
|
|
|
if not prop:
|
|
|
|
return None
|
|
|
|
value = prop.value
|
|
|
|
if not isinstance(value, list):
|
|
|
|
value = [value]
|
|
|
|
return [fdt32_to_cpu(v) for v in value]
|
|
|
|
|
2018-07-17 19:25:32 +00:00
|
|
|
def GetDatatype(node, propname, datatype):
|
|
|
|
"""Get a value of a given type from a property
|
|
|
|
|
|
|
|
Args:
|
|
|
|
node: Node object to read from
|
|
|
|
propname: property name to read
|
|
|
|
datatype: Type to read (str or int)
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
value read, or None if none
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
ValueError if datatype is not str or int
|
|
|
|
"""
|
|
|
|
if datatype == str:
|
|
|
|
return GetString(node, propname)
|
|
|
|
elif datatype == int:
|
|
|
|
return GetInt(node, propname)
|
|
|
|
raise ValueError("fdt_util internal error: Unknown data type '%s'" %
|
|
|
|
datatype)
|