dts: dtlib/edtlib: Add a syntax-based type-checking system

Property type-checking has been pretty rudimentary until now, only
checking things like the length being divisible by 4 for 'type: array',
and strings being null-terminated. In particular, no checking was done
for 'type: uint8-array', letting

  jedec-id = < 0xc8 0x28 0x17 >;

slip through when

  jedec-id = [ 0xc8 0x28 0x17 ];

was intended.

Fix it by adding a syntax-based type checker:

  1. Add Property.type, which gives a high-level type for the property,
     derived from the markers added in the previous commit.

     This includes types like TYPE_EMPTY ('foo;'),
     TYPE_NUM ('foo = < 3 >;'), TYPE_BYTES ('foo = [ 01 02 ];'),
     TYPE_STRINGS ('foo = "bar", "baz"'),
     TYPE_PHANDLE ('foo = < &bar >;'), and TYPE_COMPOUND (everything not
     recognized).

     See the Property.type docstring in dtlib for more info.

  2. Use the high-level type in
     Property.to_num()/to_string()/to_node()/etc. to verify that the
     property was assigned in an expected way for the type.

     If the assignment looks bad, give a helpful error:

       expected property 'nums' on /foo/bar in some.dts to be assigned
       with 'nums = < (number) (number) ... >', not 'nums = "oops";'

Some other related changes are included as well:

  - There's a new Property.to_bytes() function that works like accessing
    Property.bytes, except with an added check for the value being
    assigned like 'foo = [ ... ]'.

    This function solves problems like the jedec-id one.

  - There's a new Property.to_path() function for fetching the
    referenced node for assignments like 'foo = &node;', with type
    checking. (Strings are accepted too, as long as they give the path
    to an existing node.)

    This function is used for /chosen and /aliases.

  - A new 'type: phandle' type can now be given in bindings, for
    properties that are assigned like 'foo = < &node >;'.

  - Property.__str__() now displays phandles and path references as they
    were written (e.g. '< &foo >' instead of '< 0x1 >', if the
    allocated phandle happened to be 1).

  - Property.to_num() and Property.to_nums() no longer take a 'length'
    parameter, because it makes no sense with the type checking.

  - The global dtlib.to_string() and dtlib.to_strings() functions were
    removed, because they're not that useful.

  - More tests were added, along with misc. minor cleanup in various
    places.

  - Probably other stuff I forgot.

The more strict type checking in dtlib indirectly makes some parts of
edtlib more strict as well (wherever Property.to_*() is used).

Fixes: #18131

Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
Ulf Magnusson 2019-08-09 20:38:17 +02:00 committed by Kumar Gala
commit 06b746cc58
11 changed files with 596 additions and 337 deletions

View file

@ -56,7 +56,7 @@ sub-node:
#
# <property name>:
# category: <required | optional>
# type: <string | int | boolean | array | uint8-array | string-array | compound>
# type: <string | int | boolean | array | uint8-array | string-array | phandle | compound>
# description: <description of the property>
# enum:
# - <item1>
@ -64,12 +64,20 @@ sub-node:
# ...
# - <itemN>
#
# 'uint8-array' is our name for what the device tree specification calls
# 'type: uint8-array' is for what the device tree specification calls
# 'bytestring'. Properties of type 'uint8-array' should be set like this:
#
# foo = [89 AB CD];
#
# Each value is a byte in hex.
#
# 'phandle' is for properties that are assigned a single phandle, like this:
#
# foo = <&label>;
#
# 'compound' is a catch-all for more complex types, e.g.
#
# foo = <&label1 1 2 &label2 7>;
properties:
# An entry for 'compatible' must appear, as it's used to map nodes to
# bindings

View file

@ -29,7 +29,7 @@ properties:
manual defines a shared FIFO size.
phys:
type: array
type: phandle
category: optional
description: PHY provider specifier

View file

@ -29,7 +29,7 @@ properties:
manual defines a shared FIFO size.
phys:
type: array
type: phandle
category: optional
description: PHY provider specifier

View file

@ -35,7 +35,7 @@ properties:
the pull-up resistor on USB Data Positive signal.
phys:
type: array
type: phandle
category: optional
description: PHY provider specifier

View file

@ -1069,15 +1069,8 @@ class DT:
"include only characters from [0-9a-z-]"
.format(prop.name))
# Any errors this generates will already point to the property
# and /aliases
path = prop.to_string()
try:
alias2node[prop.name] = self.get_node(path)
except DTError as e:
raise DTError("/aliases: bad path for '{}': {}"
.format(prop.name, e))
# Property.to_path() already checks that the node exists
alias2node[prop.name] = prop.to_path()
self.alias2node = alias2node
@ -1354,6 +1347,32 @@ class Property:
See the to_*() methods for converting the value to other types.
type:
The type of the property, inferred from the syntax used in the
assignment. This is one of the following constants (with example
assignments):
Assignment | Property.type
-------------------+------------------------
foo; | dtlib.TYPE_EMPTY
foo = [] | dtlib.TYPE_BYTES
foo = [01 02] | dtlib.TYPE_BYTES
foo = /bits/ 8 <1> | dtlib.TYPE_BYTES
foo = <1> | dtlib.TYPE_NUM
foo = <> | dtlib.TYPE_NUMS
foo = <1 2 3> | dtlib.TYPE_NUMS
foo = <1 2>, <3> | dtlib.TYPE_NUMS
foo = "foo" | dtlib.TYPE_STRING
foo = "foo", "bar" | dtlib.TYPE_STRINGS
foo = <&label> | dtlib.TYPE_PHANDLE
foo = &label | dtlib.TYPE_PATH
*Anything else* | dtlib.TYPE_COMPOUND
*Anything else* includes properties mixing (<&label>) and node path
(&label) references with other data.
Data labels in the property value do not influence the type.
labels:
A list with all labels pointing to the property, in the same order as the
labels appear, but with duplicates removed.
@ -1394,88 +1413,218 @@ class Property:
# to be patched in after parsing.
self._markers = []
def to_num(self, length=4, signed=False):
def to_num(self, signed=False):
"""
Returns the property value interpreted as a number.
Returns the value of the property as a number.
length (default: 4):
The expected length of the value in bytes. Raises DTError if it has a
different length. This is provided as a simple type check.
Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_NUM):
Four bytes is the length of a cell, so the value of e.g.
'x = < 73 >;' can be fetched with a plain prop.to_num().
If 'length' is None, the entire property value is used, with no
length check.
foo = < 1 >;
signed (default: False):
If True, the value will be interpreted as signed rather than
unsigned.
"""
try:
return to_num(self.value, length, signed)
except DTError as e:
self._err_with_context(e)
if self.type is not TYPE_NUM:
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with '{0} = < (number) >;', not '{3}'"
.format(self.name, self.node.path,
self.node.dt.filename, self))
def to_nums(self, length=4, signed=False):
return int.from_bytes(self.value, "big", signed=signed)
def to_nums(self, signed=False):
"""
Returns the property value interpreted as a list of numbers.
Returns the value of the property as a list of numbers.
length (default: 4):
The length in bytes of each number. Raises DTError if the length of
the value is not a multiple of 'length'.
Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_NUM or TYPE_NUMS):
foo = < 1 2 ... >;
signed (default: False):
If True, the values will be interpreted as signed rather than
unsigned.
"""
try:
return to_nums(self.value, length, signed)
except DTError as e:
self._err_with_context(e)
if self.type not in (TYPE_NUM, TYPE_NUMS):
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with '{0} = < (number) (number) ... >', "
"not '{3}'"
.format(self.name, self.node.path,
self.node.dt.filename, self))
return [int.from_bytes(self.value[i:i + 4], "big", signed=signed)
for i in range(0, len(self.value), 4)]
def to_bytes(self):
"""
Returns the value of the property as a raw 'bytes', like
Property.value, except with added type checking.
Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_BYTES):
foo = [ 01 ... ];
"""
if self.type is not TYPE_BYTES:
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with '{0} = [ (byte) (byte) ... ]', "
"not '{3}'".format(self.name, self.node.path,
self.node.dt.filename, self))
return self.value
def to_string(self):
"""
Returns the property value interpreted as a string.
Returns the value of the property as a string.
Raises DTError if the value is not valid UTF-8, is not null-terminated,
or if contains more than one null terminator (the null terminator is
stripped from the returned string). Strings in Device Tree (e.g., 'x =
"foo"') are implicitly null-terminated.
Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_STRING):
foo = "string";
This function might also raise UnicodeDecodeError if the string is
not valid UTF-8.
"""
if self.type is not TYPE_STRING:
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with '{0} = \"string\"', not '{3}'"
.format(self.name, self.node.path,
self.node.dt.filename, self))
try:
return to_string(self.value)
except DTError as e:
self._err_with_context(e)
return self.value.decode("utf-8")[:-1] # Strip null
except UnicodeDecodeError:
raise DTError("value of property '{}' ({}) on {} in {} is not "
"valid UTF-8"
.format(self.name, self.value, self.node.path,
self.node.dt.filename))
def to_strings(self):
"""
Returns the property value interpreted as a list of strings.
Returns the value of the property as a list of strings.
Raises DTError if the value is not valid UTF-8 or is not
null-terminated (the null terminators are stripped from the returned
string). Strings in Device Tree (e.g., 'x = "foo"') are implicitly
null-terminated.
Raises DTError if the property was not assigned with this syntax (has
Property.type TYPE_STRING or TYPE_STRINGS):
foo = "string", "string", ... ;
Also raises DTError if any of the strings are not valid UTF-8.
"""
if self.type not in (TYPE_STRING, TYPE_STRINGS):
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with '{0} = \"string\", \"string\", ...', "
"not {3}"
.format(self.name, self.node.path,
self.node.dt.filename, self))
try:
return to_strings(self.value)
except DTError as e:
self._err_with_context(e)
return self.value.decode("utf-8").split("\0")[:-1]
except UnicodeDecodeError:
raise DTError("value of property '{}' ({}) on {} in {} is not "
"valid UTF-8"
.format(self.name, self.value, self.node.path,
self.node.dt.filename))
def to_node(self):
"""
Interprets the property value as a phandle and returns the
corresponding Node.
Returns the Node the phandle in the property points to.
Raises DTError if the value is not a valid phandle or if no node with
that phandle exists.
Raises DTError if the property was not assigned with either of these
syntaxes (has Property.type TYPE_PHANDLE or TYPE_NUM).
foo = < &bar >;
foo = < 1 >;
For the second case, DTError is raised if the phandle does not exist.
"""
phandle = self.to_num()
if self.type not in (TYPE_PHANDLE, TYPE_NUM):
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with either '{0} = < &foo >' or "
"'{0} = < (valid phandle number) >', not {3}"
.format(self.name, self.node.path,
self.node.dt.filename, self))
phandle = int.from_bytes(self.value, "big")
node = self.node.dt.phandle2node.get(phandle)
if not node:
self._err_with_context("non-existent phandle " + str(phandle))
raise DTError("the phandle given in property '{}' ({}) on {} in "
"{} does not exist"
.format(self.name, phandle, self.node.path,
self.node.dt.filename))
return node
def to_path(self):
"""
Returns the Node referenced by the path stored in the property.
Raises DTError if the property was not assigned with either of these
syntaxes (has Property.type TYPE_PATH or TYPE_STRING):
foo = &bar;
foo = "/bar";
For the second case, DTError is raised if the path does not exist.
"""
if self.type not in (TYPE_PATH, TYPE_STRING):
raise DTError("expected property '{0}' on {1} in {2} to be "
"assigned with either '{0} = &foo' or "
"'{0} = \"/path/to/node\"', not '{3}'"
.format(self.name, self.node.path,
self.node.dt.filename, self))
try:
path = self.value.decode("utf-8")[:-1]
except UnicodeDecodeError:
raise DTError("value of property '{}' ({}) on {} in {} is not "
"valid UTF-8"
.format(self.name, self.value, self.node.path,
self.node.dt.filename))
try:
return self.node.dt.get_node(path)
except DTError:
raise DTError("property '{}' on {} in {} points to the "
'non-existent node "{}"'
.format(self.name, self.node.path,
self.node.dt.filename, path))
@property
def type(self):
"""
See the class docstring.
"""
# Data labels (e.g. 'foo = label: <3>') are irrelevant, so filter them
# out
types = [marker[1] for marker in self._markers
if marker[1] != _REF_LABEL]
if not types:
return TYPE_EMPTY
if types == [_TYPE_UINT8]:
return TYPE_BYTES
if types == [_TYPE_UINT32]:
return TYPE_NUM if len(self.value) == 4 else TYPE_NUMS
# Treat 'foo = <1 2 3>, <4 5>, ...' as TYPE_NUMS too
if set(types) == {_TYPE_UINT32}:
return TYPE_NUMS
if set(types) == {_TYPE_STRING}:
return TYPE_STRING if len(types) == 1 else TYPE_STRINGS
if types == [_REF_PATH]:
return TYPE_PATH
if types == [_TYPE_UINT32, _REF_PHANDLE] and len(self.value) == 4:
return TYPE_PHANDLE
return TYPE_COMPOUND
def __str__(self):
s = "".join(label + ": " for label in self.labels) + self.name
if not self.value:
@ -1492,19 +1641,27 @@ class Property:
# End of current marker
end = next_marker[0] if next_marker else len(self.value)
if marker_type in (_TYPE_STRING, _REF_PATH):
if marker_type is _TYPE_STRING:
# end - 1 to strip off the null terminator
s += ' "{}"'.format(_decode_and_escape(
self.value[pos:end - 1]))
if end != len(self.value):
s += ","
elif marker_type is _REF_PATH:
s += " &" + ref
if end != len(self.value):
s += ","
else:
# Raw data (<>/[])
# <> or []
if marker_type is _REF_LABEL:
s += " {}:".format(ref)
elif marker_type is not _REF_PHANDLE:
# marker_type is _TYPE_UINT_*
elif marker_type is _REF_PHANDLE:
s += " &" + ref
pos += 4
# Subtle: There might be more data between the phandle and
# the next marker, so we can't 'continue' here
else: # marker_type is _TYPE_UINT*
elm_size = _TYPE_TO_N_BYTES[marker_type]
s += _N_BYTES_TO_START_STR[elm_size]
@ -1526,7 +1683,6 @@ class Property:
if pos != len(self.value):
s += ","
return s + ";"
@ -1566,14 +1722,15 @@ class Property:
def to_num(data, length=None, signed=False):
"""
Like Property.to_num(), but takes an arbitrary 'bytes' array. The value is
assumed to be in big-endian format, which is standard in Device Tree.
Converts the 'bytes' array 'data' to a number. The value is expected to be
in big-endian format, which is standard in Device Tree.
length (default: None):
The expected length of the value in bytes. See Property.to_num().
The expected length of the value in bytes, as a simple type check. If
None, the length check is skipped.
Unlike for Property.to_num(), 'length' defaults to None, meaning to skip
the length check and use the entire property value.
signed (default: False):
If True, the value will be interpreted as signed rather than unsigned.
"""
_check_is_bytes(data)
if length is not None:
@ -1601,35 +1758,20 @@ def to_nums(data, length=4, signed=False):
for i in range(0, len(data), length)]
def to_string(data):
"""
Like Property.to_string(), but takes an arbitrary 'bytes' array. The string
should be null-terminated, which is standard in Device Tree. The
null terminator is stripped from the returned value.
"""
strings = to_strings(data)
if len(strings) != 1:
raise DTError("{} contains more than one string".format(data))
return strings[0]
#
# Public constants
#
def to_strings(data):
"""
Like Property.to_strings(), but takes an arbitrary 'bytes' array. The
strings should be null-terminated, which is standard in Device Tree. The
null terminators are stripped from the returned value.
"""
_check_is_bytes(data)
try:
s = data.decode("utf-8")
except UnicodeDecodeError:
raise DTError("{} is not valid UTF-8".format(data))
if not s.endswith("\0"):
raise DTError("{} is not null-terminated".format(data))
return s.split("\0")[:-1]
# See Property.type
TYPE_EMPTY = 0
TYPE_BYTES = 1
TYPE_NUM = 2
TYPE_NUMS = 3
TYPE_STRING = 4
TYPE_STRINGS = 5
TYPE_PATH = 6
TYPE_PHANDLE = 7
TYPE_COMPOUND = 8
def _check_is_bytes(data):
@ -1640,7 +1782,7 @@ def _check_is_bytes(data):
def _check_length_positive(length):
if length < 1:
raise DTError("'size' must be greater than zero, was " + str(length))
raise DTError("'length' must be greater than zero, was " + str(length))
def _append_no_dup(lst, elm):

View file

@ -29,7 +29,7 @@ import sys
import yaml
from dtlib import DT, DTError, to_num, to_nums
from dtlib import DT, DTError, to_num, to_nums, TYPE_EMPTY
# NOTE: testedtlib.py is the test suite for this library. It can be run
# directly.
@ -137,12 +137,8 @@ class EDT:
if name not in chosen.props:
return None
path = chosen.props[name].to_string()
try:
return self._node2dev[self._dt.get_node(path)]
except DTError:
_err("{} in /chosen points to {}, which does not exist"
.format(name, path))
# to_path() checks that the node exists
return self._node2dev[chosen.props[name].to_path()]
def _init_compat2binding(self, bindings_dirs):
# Creates self._compat2binding. This is a dictionary that maps
@ -224,15 +220,29 @@ class EDT:
self.devices = []
for node in self._dt.node_iter():
# Warning: Device.__init__() relies on parent Devices being created
# before their children. This is guaranteed by node_iter().
dev = Device(self, node)
# Warning: We depend on parent Devices being created before their
# children. This is guaranteed by node_iter().
dev = Device()
dev.edt = self
dev._node = node
dev._init_binding()
dev._init_regs()
dev._set_instance_no()
self.devices.append(dev)
self._node2dev[node] = dev
for dev in self.devices:
# These depend on all Device objects having been created, so we do
# them separately
# Device._init_props() depends on all Device objects having been
# created, due to 'type: phandle', so we run it separately.
# Property.val is set to the pointed-to Device instance for
# phandles, which must exist.
dev._init_props()
for dev in self.devices:
# These also depend on all Device objects having been created, and
# might also depend on all Device.props having been initialized
# (_init_clocks() does as of writing).
dev._init_interrupts()
dev._init_gpios()
dev._init_pwms()
@ -440,21 +450,6 @@ class Device:
"binding " + self.binding_path if self.binding_path
else "no binding")
def __init__(self, edt, node):
"Private constructor. Not meant to be called by clients."
# Interrupts, GPIOs, PWMs, io-channels, and clocks are
# initialized separately, because they depend on all Devices
# existing
self.edt = edt
self._node = node
self._init_binding()
self._init_props()
self._init_regs()
self._set_instance_no()
def _init_binding(self):
# Initializes Device.matching_compat, Device._binding, and
# Device.binding_path.
@ -578,12 +573,17 @@ class Device:
# _init_prop() helper for getting the property's value
node = self._node
prop = node.props.get(name)
if prop_type == "boolean":
# True/False
return name in node.props
if not prop:
return False
if prop.type is not TYPE_EMPTY:
_err("'{0}' in {1!r} is defined with 'type: boolean' in {2}, "
"but is assigned a value ('{3}') instead of being empty "
"('{0};')".format(name, node, self.binding_path, prop))
return True
prop = node.props.get(name)
if not prop:
if not optional and self.enabled:
_err("'{}' is marked as required in 'properties:' in {}, but "
@ -599,7 +599,7 @@ class Device:
return prop.to_nums()
if prop_type == "uint8-array":
return prop.value # Plain 'bytes'
return prop.to_bytes()
if prop_type == "string":
return prop.to_string()
@ -607,6 +607,9 @@ class Device:
if prop_type == "string-array":
return prop.to_strings()
if prop_type == "phandle":
return self.edt._node2dev[prop.to_node()]
_err("'{}' in 'properties:' in {} has unknown type '{}'"
.format(name, self.binding_path, prop_type))
@ -1013,8 +1016,9 @@ class Property:
if missing. Trailing whitespace (including newlines) is removed.
val:
The value of the property, with the format determined by the 'type:'
key from the binding
The value of the property, with the format determined by the 'type:' key
from the binding. For 'type: phandle' properties, this is the pointed-to
Device instance.
enum_index:
The index of the property's value in the 'enum:' list in the binding, or

View file

@ -154,6 +154,10 @@ def write_props(dev):
if prop.name[0] == "#" or prop.name.endswith("-map"):
continue
# Skip phandles
if isinstance(prop.val, edtlib.Device):
continue
# Skip properties that we handle elsewhere
if prop.name in {"reg", "interrupts", "compatible", "interrupt-controller",
"gpio-controller"}:

View file

@ -8,6 +8,12 @@ properties:
constraint: "props"
type: string-array
nonexistent-boolean:
type: boolean
existent-boolean:
type: boolean
int:
type: int
@ -22,3 +28,6 @@ properties:
string-array:
type: string-array
phandle-ref:
type: phandle

View file

@ -290,14 +290,19 @@
props {
compatible = "props";
existent-boolean;
int = <1>;
array = <1 2 3>;
uint8-array = [ 12 34 ];
string = "foo";
string-array = "foo", "bar", "baz";
phandle-ref = < &{/props/node} >;
// Does not appear in the binding, so won't create an entry in
// Device.props
not-speced = <0>;
node {
};
};
//

View file

@ -422,7 +422,7 @@ l3: &l1 {
/dts-v1/;
/ {
a = l01: l02: < l03: 0x1 l04: l05: 0x2 l06: l07: l08: >, [ l09: 03 l10: l11: 04 l12: l13: l14: ], "A";
a = l01: l02: < l03: &node l04: l05: 0x2 l06: l07: l08: >, [ l09: 03 l10: l11: 04 l12: l13: l14: ], "A";
b = < 0x0 l23: l24: >;
node: node {
phandle = < 0x1 >;
@ -504,13 +504,13 @@ l3: &l1 {
/dts-v1/;
/ {
a = "/abc";
b = [ 01 ], "/abc";
c = [ 01 ], "/abc", < 0x2 >;
d = "/abc";
a = &label;
b = [ 01 ], &label;
c = [ 01 ], &label, < 0x2 >;
d = &{/abc};
label: abc {
e = "/abc";
f = "/abc";
e = &label;
f = &{/abc};
};
};
""")
@ -541,7 +541,6 @@ l3: &l1 {
# Test phandles
#
# Check that existing phandles are used (and not reused)
verify_parse("""
/dts-v1/;
@ -571,7 +570,7 @@ l3: &l1 {
/dts-v1/;
/ {
x = < 0x2 0x4 0xff >;
x = < &a &{/b} &c >;
dummy1 {
phandle = < 0x1 >;
};
@ -617,10 +616,10 @@ l3: &l1 {
phandle = < 0x1 >;
};
a {
foo: phandle = < 0x2 >;
foo: phandle = < &{/a} >;
};
label: b {
bar: phandle = < 0x3 >;
bar: phandle = < &label >;
};
};
""")
@ -765,7 +764,7 @@ l3: &l1 {
/dts-v1/;
/ {
x = [ FF FF ], "/abc", < 0xff 0x1 0xff 0x1 >, "/abc", [ FF FF ], "abc";
x = [ FF FF ], &abc, < 0xff &abc 0xff &abc >, &abc, [ FF FF ], "abc";
abc: abc {
phandle = < 0x1 >;
};
@ -1007,7 +1006,7 @@ y /include/ "via-include-path-1"
/dts-v1/;
/ {
x = < 0x1 >, "/referenced2";
x = < &{/referenced} >, &referenced2;
referenced {
phandle = < 0x1 >;
};
@ -1299,7 +1298,7 @@ foo
alias1 = &l1;
alias2 = &l2;
alias3 = &{/sub/node3};
alias4 = [2F 6E 6F 64 65 34 00]; // "/node4";
alias4 = &{/node4};
};
l1: node1 {
@ -1323,7 +1322,6 @@ foo
verify_alias_target("alias1", "node1")
verify_alias_target("alias2", "node2")
verify_alias_target("alias3", "node3")
verify_alias_target("alias4", "node4")
verify_path_is("alias4/node5", "node5")
verify_path_error("alias4/node5/node6",
@ -1332,6 +1330,28 @@ foo
verify_error("""
/dts-v1/;
/ {
aliases {
a = [ 00 ];
};
};
""",
"expected property 'a' on /aliases in .tmp.dts to be assigned with either 'a = &foo' or 'a = \"/path/to/node\"', not 'a = [ 00 ];'")
verify_error(r"""
/dts-v1/;
/ {
aliases {
a = "\xFF";
};
};
""",
r"value of property 'a' (b'\xff\x00') on /aliases in .tmp.dts is not valid UTF-8")
verify_error("""
/dts-v1/;
/ {
aliases {
A = "/aliases";
@ -1343,58 +1363,132 @@ foo
verify_error(r"""
/dts-v1/;
/ {
aliases {
a = "\xFF";
};
};
""",
r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
verify_error(r"""
/dts-v1/;
/ {
aliases {
a = [ 41 ]; // "A"
};
};
""",
"b'A' is not null-terminated (for property 'a' on /aliases)")
verify_error(r"""
/dts-v1/;
/ {
aliases {
a = "/missing";
};
};
""",
"/aliases: bad path for 'a': component 1 ('missing') in path '/missing' does not exist")
"property 'a' on /aliases in .tmp.dts points to the non-existent node \"/missing\"")
#
# Test to_{num,nums,string,strings,node}()
# Test Property.type
#
def verify_to_num(prop, size, signed, expected):
def verify_type(prop, expected):
actual = dt.root.props[prop].type
if actual != expected:
fail("expected {} to have type {}, had type {}"
.format(prop, expected, actual))
dt = parse("""
/dts-v1/;
/ {
empty;
bytes1 = [ ];
bytes2 = [ 01 ];
bytes3 = [ 01 02 ];
bytes4 = foo: [ 01 bar: 02 ];
bytes5 = /bits/ 8 < 1 2 3 >;
num = < 1 >;
nums1 = < >;
nums2 = < >, < >;
nums3 = < 1 2 >;
nums4 = < 1 2 >, < 3 >, < 4 >;
string = "foo";
strings = "foo", "bar";
phandle1 = < &node >;
phandle2 = < &{/node} >;
path1 = &node;
path2 = &{/node};
compound1 = < 1 >, [ 02 ];
compound2 = "foo", < >;
compound3 = < 1 &{/node} 2>;
node: node {
};
};
""")
verify_type("empty", dtlib.TYPE_EMPTY)
verify_type("bytes1", dtlib.TYPE_BYTES)
verify_type("bytes2", dtlib.TYPE_BYTES)
verify_type("bytes3", dtlib.TYPE_BYTES)
verify_type("bytes4", dtlib.TYPE_BYTES)
verify_type("bytes5", dtlib.TYPE_BYTES)
verify_type("num", dtlib.TYPE_NUM)
verify_type("nums1", dtlib.TYPE_NUMS)
verify_type("nums2", dtlib.TYPE_NUMS)
verify_type("nums3", dtlib.TYPE_NUMS)
verify_type("nums4", dtlib.TYPE_NUMS)
verify_type("string", dtlib.TYPE_STRING)
verify_type("strings", dtlib.TYPE_STRINGS)
verify_type("phandle1", dtlib.TYPE_PHANDLE)
verify_type("phandle2", dtlib.TYPE_PHANDLE)
verify_type("path1", dtlib.TYPE_PATH)
verify_type("path2", dtlib.TYPE_PATH)
verify_type("compound1", dtlib.TYPE_COMPOUND)
verify_type("compound2", dtlib.TYPE_COMPOUND)
verify_type("compound3", dtlib.TYPE_COMPOUND)
#
# Test Property.to_{num,nums,string,strings,node}()
#
dt = parse(r"""
/dts-v1/;
/ {
u = < 1 >;
s = < 0xFFFFFFFF >;
u8 = /bits/ 8 < 1 >;
u16 = /bits/ 16 < 1 2 >;
u64 = /bits/ 64 < 1 >;
bytes = [ 01 02 03 ];
empty;
zero = < >;
two_u = < 1 2 >;
two_s = < 0xFFFFFFFF 0xFFFFFFFE >;
three_u = < 1 2 3 >;
three_u_split = < 1 >, < 2 >, < 3 >;
empty_string = "";
string = "foo\tbar baz";
invalid_string = "\xff";
strings = "foo", "bar", "baz";
invalid_strings = "foo", "\xff", "bar";
ref = <&{/target}>;
manualref = < 100 >;
missingref = < 123 >;
path = &{/target};
manualpath = "/target";
missingpath = "/missing";
target {
phandle = < 100 >;
};
};
""")
# Test Property.to_num()
def verify_to_num(prop, signed, expected):
try:
actual = dt.root.props[prop].to_num(size, signed)
actual = dt.root.props[prop].to_num(signed)
except dtlib.DTError as e:
fail("failed to convert {} to {} number with {} bytes: {}"
.format(prop, "a signed" if signed else "an unsigned", size,
e))
fail("failed to convert '{}' to {} number: {}"
.format(prop, "a signed" if signed else "an unsigned", e))
if actual != expected:
fail("expected {} to have the {} numeric value {:#x}, had the "
"value {:#x}".format(prop, "signed" if signed else "unsigned",
expected, actual))
def verify_to_num_error(prop, size, msg):
prefix = "expected {} converted from {} bytes to generate the error " \
"'{}', generated".format(prop, size, msg)
def verify_to_num_error(prop, msg):
prefix = "expected fetching '{}' as a number to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_num(size)
dt.root.props[prop].to_num()
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
@ -1402,23 +1496,36 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
def verify_to_nums(prop, size, signed, expected):
verify_to_num("u", False, 1)
verify_to_num("u", True, 1)
verify_to_num("s", False, 0xFFFFFFFF)
verify_to_num("s", True, -1)
verify_to_num_error("two_u", "expected property 'two_u' on / in .tmp.dts to be assigned with 'two_u = < (number) >;', not 'two_u = < 0x1 0x2 >;'")
verify_to_num_error("u8", "expected property 'u8' on / in .tmp.dts to be assigned with 'u8 = < (number) >;', not 'u8 = [ 01 ];'")
verify_to_num_error("u16", "expected property 'u16' on / in .tmp.dts to be assigned with 'u16 = < (number) >;', not 'u16 = /bits/ 16 < 0x1 0x2 >;'")
verify_to_num_error("u64", "expected property 'u64' on / in .tmp.dts to be assigned with 'u64 = < (number) >;', not 'u64 = /bits/ 64 < 0x1 >;'")
verify_to_num_error("string", "expected property 'string' on / in .tmp.dts to be assigned with 'string = < (number) >;', not 'string = \"foo\\tbar baz\";'")
# Test Property.to_nums()
def verify_to_nums(prop, signed, expected):
try:
actual = dt.root.props[prop].to_nums(size, signed)
actual = dt.root.props[prop].to_nums(signed)
except dtlib.DTError as e:
fail("failed to convert {} to {} numbers with {} bytes each: {}"
.format(prop, "signed" if signed else "unsigned", size, e))
fail("failed to convert '{}' to {} numbers: {}"
.format(prop, "signed" if signed else "unsigned", e))
if actual != expected:
fail("expected {} to give the {} numbers {} for size {}, gave {}"
fail("expected {} to give the {} numbers {}, gave {}"
.format(prop, "signed" if signed else "unsigned", expected,
size, actual))
actual))
def verify_to_nums_error(prop, size, msg):
prefix = "expected {} converted to numbers with {} bytes each to " \
"generate the error '{}', generated".format(prop, size, msg)
def verify_to_nums_error(prop, msg):
prefix = "expected converting '{}' to numbers to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_nums(size)
dt.root.props[prop].to_nums()
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
@ -1426,11 +1533,35 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
def verify_raw_to_num_error(fn, data, size, msg):
prefix = "expected {}() called with data='{}', size='{}' to generate " \
"the error '{}', generated".format(fn.__name__, data, size, msg)
verify_to_nums("zero", False, [])
verify_to_nums("u", False, [1])
verify_to_nums("two_u", False, [1, 2])
verify_to_nums("two_u", True, [1, 2])
verify_to_nums("two_s", False, [0xFFFFFFFF, 0xFFFFFFFE])
verify_to_nums("two_s", True, [-1, -2])
verify_to_nums("three_u", False, [1, 2, 3])
verify_to_nums("three_u_split", False, [1, 2, 3])
verify_to_nums_error("empty", "expected property 'empty' on / in .tmp.dts to be assigned with 'empty = < (number) (number) ... >', not 'empty;'")
verify_to_nums_error("string", "expected property 'string' on / in .tmp.dts to be assigned with 'string = < (number) (number) ... >', not 'string = \"foo\\tbar baz\";'")
# Test Property.to_bytes()
def verify_to_bytes(prop, expected):
try:
dtlib.to_num(data, size)
actual = dt.root.props[prop].to_bytes()
except dtlib.DTError as e:
fail("failed to convert '{}' to bytes: {}".format(prop, e))
if actual != expected:
fail("expected {} to give the bytes {}, gave {}"
.format(prop, expected, actual))
def verify_to_bytes_error(prop, msg):
prefix = "expected converting '{}' to bytes to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_bytes()
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
@ -1438,18 +1569,26 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
verify_to_bytes("u8", b"\x01")
verify_to_bytes("bytes", b"\x01\x02\x03")
verify_to_bytes_error("u16", "expected property 'u16' on / in .tmp.dts to be assigned with 'u16 = [ (byte) (byte) ... ]', not 'u16 = /bits/ 16 < 0x1 0x2 >;'")
verify_to_bytes_error("empty", "expected property 'empty' on / in .tmp.dts to be assigned with 'empty = [ (byte) (byte) ... ]', not 'empty;'")
# Test Property.to_string()
def verify_to_string(prop, expected):
try:
actual = dt.root.props[prop].to_string()
except dtlib.DTError as e:
fail("failed to convert {} to string: {}".format(prop, e))
fail("failed to convert '{}' to string: {}".format(prop, e))
if actual != expected:
fail("expected {} to have the value '{}', had the value '{}'"
.format(prop, expected, actual))
def verify_to_string_error(prop, msg):
prefix = "expected converting {} to string to generate the error " \
prefix = "expected converting '{}' to string to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_string()
@ -1460,33 +1599,30 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
def verify_raw_to_string_error(data, msg):
prefix = "expected to_string() called with data='{}' to generate " \
"the error '{}', generated".format(data, msg)
try:
dtlib.to_string(data)
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
fail("{} the error '{}'".format(prefix, e))
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
verify_to_string("empty_string", "")
verify_to_string("string", "foo\tbar baz")
verify_to_string_error("u", "expected property 'u' on / in .tmp.dts to be assigned with 'u = \"string\"', not 'u = < 0x1 >;'")
verify_to_string_error("strings", "expected property 'strings' on / in .tmp.dts to be assigned with 'strings = \"string\"', not 'strings = \"foo\", \"bar\", \"baz\";'")
verify_to_string_error("invalid_string", r"value of property 'invalid_string' (b'\xff\x00') on / in .tmp.dts is not valid UTF-8")
# Test Property.to_strings()
def verify_to_strings(prop, expected):
try:
actual = dt.root.props[prop].to_strings()
except dtlib.DTError as e:
fail("failed to convert {} to strings: {}".format(prop, e))
fail("failed to convert '{}' to strings: {}".format(prop, e))
if actual != expected:
fail("expected {} to have the value '{}', had the value '{}'"
.format(prop, expected, actual))
def verify_to_strings_error(prop, msg):
prefix = "expected converting {} to strings to generate the error " \
prefix = "expected converting '{}' to strings to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_string()
dt.root.props[prop].to_strings()
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
@ -1494,18 +1630,27 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
verify_to_strings("empty_string", [""])
verify_to_strings("string", ["foo\tbar baz"])
verify_to_strings("strings", ["foo", "bar", "baz"])
verify_to_strings_error("u", "expected property 'u' on / in .tmp.dts to be assigned with 'u = \"string\", \"string\", ...', not u = < 0x1 >;")
verify_to_strings_error("invalid_strings", r"value of property 'invalid_strings' (b'foo\x00\xff\x00bar\x00') on / in .tmp.dts is not valid UTF-8")
# Test Property.to_node()
def verify_to_node(prop, path):
try:
actual = dt.root.props[prop].to_node().path
except dtlib.DTError as e:
fail("failed to convert {} to node: {}".format(prop, e))
fail("failed to convert '{}' to node: {}".format(prop, e))
if actual != path:
fail("expected {} to point to {}, pointed to {}"
.format(prop, path, actual))
def verify_to_node_error(prop, msg):
prefix = "expected converting {} to node to generate the error " \
prefix = "expected converting '{}' to a node to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_node()
@ -1516,140 +1661,82 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
dt = parse(r"""
/dts-v1/;
verify_to_node("ref", "/target")
verify_to_node("manualref", "/target")
/ {
empty;
u1 = /bits/ 8 < 0x01 >;
u2 = /bits/ 8 < 0x01 0x02 >;
u3 = /bits/ 8 < 0x01 0x02 0x03 >;
u4 = /bits/ 8 < 0x01 0x02 0x03 0x04 >;
s1 = /bits/ 8 < 0xFF >;
s2 = /bits/ 8 < 0xFF 0xFE >;
s3 = /bits/ 8 < 0xFF 0xFF 0xFD >;
s4 = /bits/ 8 < 0xFF 0xFF 0xFF 0xFC >;
empty_string = "";
string = "foo\tbar baz";
invalid_string = "\xff";
non_null_terminated_string = [ 41 ]; // A
strings = "foo", "bar", "baz";
invalid_strings = "foo", "\xff", "bar";
non_null_terminated_strings = "foo", "bar", [ 01 ];
ref = <&{/target}>;
missingref = < 123 >;
badref;
verify_to_node_error("string", "expected property 'string' on / in .tmp.dts to be assigned with either 'string = < &foo >' or 'string = < (valid phandle number) >', not string = \"foo\\tbar baz\";")
verify_to_node_error("missingref", "the phandle given in property 'missingref' (123) on / in .tmp.dts does not exist")
target {
};
};
""")
# Test Property.to_path()
# Test to_num()
def verify_to_path(prop, path):
try:
actual = dt.root.props[prop].to_path().path
except dtlib.DTError as e:
fail("failed to convert '{}' to path: {}".format(prop, e))
verify_to_num("u1", 1, False, 0x01)
verify_to_num("u2", 2, False, 0x0102)
verify_to_num("u3", 3, False, 0x010203)
verify_to_num("u4", 4, False, 0x01020304)
verify_to_num("s1", 1, False, 0xFF)
verify_to_num("s2", 2, False, 0xFFFE)
verify_to_num("s3", 3, False, 0xFFFFFD)
verify_to_num("s4", 4, False, 0xFFFFFFFC)
if actual != path:
fail("expected {} to contain the path {}, contained {}"
.format(prop, path, actual))
verify_to_num("u1", 1, True, 0x01)
verify_to_num("u2", 2, True, 0x0102)
verify_to_num("u3", 3, True, 0x010203)
verify_to_num("u4", 4, True, 0x01020304)
verify_to_num("s1", 1, True, -1)
verify_to_num("s2", 2, True, -2)
verify_to_num("s3", 3, True, -3)
verify_to_num("s4", 4, True, -4)
def verify_to_path_error(prop, msg):
prefix = "expected converting '{}' to a path to generate the error " \
"'{}', generated".format(prop, msg)
try:
dt.root.props[prop].to_path()
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
fail("{} the error '{}'".format(prefix, e))
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
verify_to_num("u1", None, False, 0x01)
verify_to_num("u2", None, False, 0x0102)
verify_to_num("u3", None, False, 0x010203)
verify_to_num("u4", None, False, 0x01020304)
verify_to_num("s1", None, False, 0xFF)
verify_to_num("s2", None, False, 0xFFFE)
verify_to_num("s3", None, False, 0xFFFFFD)
verify_to_num("s4", None, False, 0xFFFFFFFC)
verify_to_path("path", "/target")
verify_to_path("manualpath", "/target")
verify_to_num("u1", None, True, 0x01)
verify_to_num("u2", None, True, 0x0102)
verify_to_num("u3", None, True, 0x010203)
verify_to_num("u4", None, True, 0x01020304)
verify_to_num("s1", None, True, -1)
verify_to_num("s2", None, True, -2)
verify_to_num("s3", None, True, -3)
verify_to_num("s4", None, True, -4)
verify_to_path_error("u", "expected property 'u' on / in .tmp.dts to be assigned with either 'u = &foo' or 'u = \"/path/to/node\"', not 'u = < 0x1 >;'")
verify_to_path_error("missingpath", "property 'missingpath' on / in .tmp.dts points to the non-existent node \"/missing\"")
verify_to_num_error("u1", 0, "'size' must be greater than zero, was 0 (for property 'u1' on /)")
verify_to_num_error("u1", -1, "'size' must be greater than zero, was -1 (for property 'u1' on /)")
verify_to_num_error("u1", 2, r"b'\x01' is 1 bytes long, expected 2 (for property 'u1' on /)")
verify_to_num_error("u2", 1, r"b'\x01\x02' is 2 bytes long, expected 1 (for property 'u2' on /)")
# Test top-level to_num() and to_nums()
def verify_raw_to_num(fn, prop, length, signed, expected):
try:
actual = fn(dt.root.props[prop].value, length, signed)
except dtlib.DTError as e:
fail("failed to convert '{}' to {} number(s) with {}: {}"
.format(prop, "signed" if signed else "unsigned",
fn.__name__, e))
if actual != expected:
fail("expected {}(<{}>, {}, {}) to be {}, was {}"
.format(fn.__name__, prop, length, signed, expected, actual))
def verify_raw_to_num_error(fn, data, length, msg):
prefix = "expected {}() called with data='{}', length='{}' to " \
"generate the error '{}', generated" \
.format(fn.__name__, data, length, msg)
try:
fn(data, length)
fail(prefix + " no error")
except dtlib.DTError as e:
if str(e) != msg:
fail("{} the error '{}'".format(prefix, e))
except Exception as e:
fail("{} the non-DTError '{}'".format(prefix, e))
verify_raw_to_num(dtlib.to_num, "u", None, False, 1)
verify_raw_to_num(dtlib.to_num, "u", 4, False, 1)
verify_raw_to_num(dtlib.to_num, "s", None, False, 0xFFFFFFFF)
verify_raw_to_num(dtlib.to_num, "s", None, True, -1)
verify_raw_to_num(dtlib.to_nums, "empty", 4, False, [])
verify_raw_to_num(dtlib.to_nums, "u16", 2, False, [1, 2])
verify_raw_to_num(dtlib.to_nums, "two_s", 4, False, [0xFFFFFFFF, 0xFFFFFFFE])
verify_raw_to_num(dtlib.to_nums, "two_s", 4, True, [-1, -2])
verify_raw_to_num_error(dtlib.to_num, 0, 0, "'0' has type 'int', expected 'bytes'")
verify_raw_to_num_error(dtlib.to_num, b"", 0, "'size' must be greater than zero, was 0")
# Test to_nums()
verify_to_nums("empty", 1, False, [])
verify_to_nums("u1", 1, False, [1])
verify_to_nums("u2", 1, False, [1, 2])
verify_to_nums("u3", 1, False, [1, 2, 3])
verify_to_nums("u4", 1, False, [1, 2, 3, 4])
verify_to_nums("s1", 1, False, [0xFF])
verify_to_nums("s2", 1, False, [0xFF, 0xFE])
verify_to_nums("s3", 1, False, [0xFF, 0xFF, 0xFD])
verify_to_nums("s4", 1, False, [0xFF, 0xFF, 0xFF, 0xFC])
verify_to_nums("u2", 2, False, [0x0102])
verify_to_nums("u4", 2, False, [0x0102, 0x0304])
verify_to_nums("u1", 1, True, [1])
verify_to_nums("u2", 1, True, [1, 2])
verify_to_nums("u3", 1, True, [1, 2, 3])
verify_to_nums("u4", 1, True, [1, 2, 3, 4])
verify_to_nums("s1", 1, True, [-1])
verify_to_nums("s2", 1, True, [-1, -2])
verify_to_nums("s3", 1, True, [-1, -1, -3])
verify_to_nums("s4", 1, True, [-1, -1, -1, -4])
verify_to_nums("s2", 2, True, [-2])
verify_to_nums("s4", 2, True, [-1, -4])
verify_to_nums_error("u1", 0, "'size' must be greater than zero, was 0 (for property 'u1' on /)")
verify_to_nums_error("u1", 2, r"b'\x01' is 1 bytes long, expected a length that's a multiple of 2 (for property 'u1' on /)")
verify_to_nums_error("u2", 3, r"b'\x01\x02' is 2 bytes long, expected a length that's a multiple of 3 (for property 'u2' on /)")
verify_raw_to_num_error(dtlib.to_num, b"", 0, "'length' must be greater than zero, was 0")
verify_raw_to_num_error(dtlib.to_nums, 0, 0, "'0' has type 'int', expected 'bytes'")
verify_raw_to_num_error(dtlib.to_nums, b"", 0, "'size' must be greater than zero, was 0")
# Test to_string()
verify_to_string("empty_string", "")
verify_to_string("string", "foo\tbar baz")
verify_to_string_error("invalid_string", r"b'\xff\x00' is not valid UTF-8 (for property 'invalid_string' on /)")
verify_to_string_error("non_null_terminated_string", "b'A' is not null-terminated (for property 'non_null_terminated_string' on /)")
verify_raw_to_string_error(0, "'0' has type 'int', expected 'bytes'")
# Test to_strings()
verify_to_strings("empty_string", [""])
verify_to_strings("string", ["foo\tbar baz"])
verify_to_strings("strings", ["foo", "bar", "baz"])
verify_to_strings_error("invalid_strings", r"b'foo\x00\xff\x00bar\x00' is not valid UTF-8 (for property 'invalid_strings' on /)")
verify_to_strings_error("non_null_terminated_strings", r"b'foo\x00bar\x00\x01' is not null-terminated (for property 'non_null_terminated_strings' on /)")
# Test to_node()
verify_to_node("ref", "/target")
verify_to_node_error("missingref", "non-existent phandle 123 (for property 'missingref' on /)")
verify_to_node_error("badref", "b'' is 0 bytes long, expected 4 (for property 'badref' on /)")
verify_raw_to_num_error(dtlib.to_nums, b"", 0, "'length' must be greater than zero, was 0")
#
# Test duplicate label error
@ -1820,7 +1907,7 @@ r"b'\xff\x00' is not valid UTF-8 (for property 'a' on /aliases)")
/ {
label: foo {
x = "/foo", "/foo", < 0x1 >;
x = &{/foo}, &label, < &label >;
phandle = < 0x1 >;
};
};
@ -1907,7 +1994,7 @@ l1: l2: /memreserve/ 0x0000000000000002 0x0000000000000004;
/dts-v1/;
/ {
aA0,._+*#?- = "/aA0,._+*#?@-", "/aA0,._+*#?@-";
aA0,._+*#?- = &_, &{/aA0,._+*#?@-};
+ = [ 00 ];
* = [ 02 ];
- = [ 01 ];

View file

@ -120,7 +120,7 @@ def run():
#
verify_streq(edt.get_dev("/props").props,
r"{'compatible': <Property, name: compatible, value: ['props']>, 'int': <Property, name: int, value: 1>, 'array': <Property, name: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, value: b'\x124'>, 'string': <Property, name: string, value: 'foo'>, 'string-array': <Property, name: string-array, value: ['foo', 'bar', 'baz']>}")
r"{'compatible': <Property, name: compatible, value: ['props']>, 'nonexistent-boolean': <Property, name: nonexistent-boolean, value: False>, 'existent-boolean': <Property, name: existent-boolean, value: True>, 'int': <Property, name: int, value: 1>, 'array': <Property, name: array, value: [1, 2, 3]>, 'uint8-array': <Property, name: uint8-array, value: b'\x124'>, 'string': <Property, name: string, value: 'foo'>, 'string-array': <Property, name: string-array, value: ['foo', 'bar', 'baz']>, 'phandle-ref': <Property, name: phandle-ref, value: <Device /props/node in 'test.dts', no binding>>}")
#
# Test having multiple directories with bindings, with a different .dts file