From 06b746cc583bd765a99401af82a6b162abe91129 Mon Sep 17 00:00:00 2001 From: Ulf Magnusson Date: Fri, 9 Aug 2019 20:38:17 +0200 Subject: [PATCH] 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 --- dts/bindings/binding-template.yaml | 12 +- dts/bindings/usb/st,stm32-otgfs.yaml | 2 +- dts/bindings/usb/st,stm32-otghs.yaml | 2 +- dts/bindings/usb/st,stm32-usb.yaml | 2 +- scripts/dts/dtlib.py | 328 +++++++++++++----- scripts/dts/edtlib.py | 70 ++-- scripts/dts/gen_defines.py | 4 + scripts/dts/test-bindings/props.yaml | 9 + scripts/dts/test.dts | 5 + scripts/dts/testdtlib.py | 497 ++++++++++++++++----------- scripts/dts/testedtlib.py | 2 +- 11 files changed, 596 insertions(+), 337 deletions(-) diff --git a/dts/bindings/binding-template.yaml b/dts/bindings/binding-template.yaml index 8d3eba76be1..2a278def02b 100644 --- a/dts/bindings/binding-template.yaml +++ b/dts/bindings/binding-template.yaml @@ -56,7 +56,7 @@ sub-node: # # : # category: -# type: +# type: # description: # enum: # - @@ -64,12 +64,20 @@ sub-node: # ... # - # -# '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 diff --git a/dts/bindings/usb/st,stm32-otgfs.yaml b/dts/bindings/usb/st,stm32-otgfs.yaml index 775bf9d00b6..85b20a7acbf 100644 --- a/dts/bindings/usb/st,stm32-otgfs.yaml +++ b/dts/bindings/usb/st,stm32-otgfs.yaml @@ -29,7 +29,7 @@ properties: manual defines a shared FIFO size. phys: - type: array + type: phandle category: optional description: PHY provider specifier diff --git a/dts/bindings/usb/st,stm32-otghs.yaml b/dts/bindings/usb/st,stm32-otghs.yaml index d52f890f003..5cb82e6db87 100644 --- a/dts/bindings/usb/st,stm32-otghs.yaml +++ b/dts/bindings/usb/st,stm32-otghs.yaml @@ -29,7 +29,7 @@ properties: manual defines a shared FIFO size. phys: - type: array + type: phandle category: optional description: PHY provider specifier diff --git a/dts/bindings/usb/st,stm32-usb.yaml b/dts/bindings/usb/st,stm32-usb.yaml index b82f236077b..bfa586c081e 100644 --- a/dts/bindings/usb/st,stm32-usb.yaml +++ b/dts/bindings/usb/st,stm32-usb.yaml @@ -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 diff --git a/scripts/dts/dtlib.py b/scripts/dts/dtlib.py index d8e86320877..f14a378194b 100644 --- a/scripts/dts/dtlib.py +++ b/scripts/dts/dtlib.py @@ -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): diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index fc20a3b3dba..08d6c6bd27f 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -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 diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 754c5ac52f1..7736b50b157 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -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"}: diff --git a/scripts/dts/test-bindings/props.yaml b/scripts/dts/test-bindings/props.yaml index 2990b60a264..e0752918798 100644 --- a/scripts/dts/test-bindings/props.yaml +++ b/scripts/dts/test-bindings/props.yaml @@ -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 diff --git a/scripts/dts/test.dts b/scripts/dts/test.dts index b3d4cd28665..e90a615642c 100644 --- a/scripts/dts/test.dts +++ b/scripts/dts/test.dts @@ -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 { + }; }; // diff --git a/scripts/dts/testdtlib.py b/scripts/dts/testdtlib.py index ba635124f28..365f81037ea 100755 --- a/scripts/dts/testdtlib.py +++ b/scripts/dts/testdtlib.py @@ -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 ]; diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py index 4bc5677a498..449aae7718d 100755 --- a/scripts/dts/testedtlib.py +++ b/scripts/dts/testedtlib.py @@ -120,7 +120,7 @@ def run(): # verify_streq(edt.get_dev("/props").props, - r"{'compatible': , 'int': , 'array': , 'uint8-array': , 'string': , 'string-array': }") + r"{'compatible': , 'nonexistent-boolean': , 'existent-boolean': , 'int': , 'array': , 'uint8-array': , 'string': , 'string-array': , 'phandle-ref': >}") # # Test having multiple directories with bindings, with a different .dts file