New kernel drivers and device migrations (#563)
This commit is contained in:
committed by
GitHub
parent
955416dac8
commit
8af6204ba1
@@ -46,6 +46,8 @@ def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
|
||||
description=details.get('description', '').strip(),
|
||||
default=details.get('default', None),
|
||||
element_type=details.get('element-type', None),
|
||||
min=details.get('min', None),
|
||||
max=details.get('max', None),
|
||||
)
|
||||
properties_dict[name] = prop
|
||||
filename = os.path.basename(file_path)
|
||||
|
||||
@@ -118,6 +118,25 @@ def resolve_phandle_array_entries(device_property, devices):
|
||||
entries.append(str(item))
|
||||
return entries
|
||||
|
||||
def validate_property_range(device: Device, binding_property, value) -> None:
|
||||
"""Enforces a binding's optional min/max on int-typed properties. Silently skips
|
||||
values that aren't parseable as a plain integer literal (e.g. a passed-through
|
||||
symbolic #define) - those can't be range-checked at compile time."""
|
||||
if binding_property.min is None and binding_property.max is None:
|
||||
return
|
||||
try:
|
||||
numeric_value = int(value, 0) if isinstance(value, str) else int(value)
|
||||
except (TypeError, ValueError):
|
||||
return
|
||||
if binding_property.min is not None and numeric_value < binding_property.min:
|
||||
raise DevicetreeException(
|
||||
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is below minimum {binding_property.min}"
|
||||
)
|
||||
if binding_property.max is not None and numeric_value > binding_property.max:
|
||||
raise DevicetreeException(
|
||||
f"Device '{device.node_name}' property '{binding_property.name}' value {numeric_value} is above maximum {binding_property.max}"
|
||||
)
|
||||
|
||||
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
|
||||
compatible_property = find_device_property(device, "compatible")
|
||||
if compatible_property is None:
|
||||
@@ -168,6 +187,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
||||
|
||||
if device_property is None:
|
||||
if binding_property.default is not None:
|
||||
validate_property_range(device, binding_property, binding_property.default)
|
||||
temp_prop = DeviceProperty(
|
||||
name=binding_property.name,
|
||||
type=binding_property.type,
|
||||
@@ -184,6 +204,7 @@ def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], de
|
||||
else:
|
||||
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
|
||||
else:
|
||||
validate_property_range(device, binding_property, device_property.value)
|
||||
result.append(property_to_string(device_property, devices))
|
||||
|
||||
return result, phandle_arrays
|
||||
|
||||
@@ -40,6 +40,8 @@ class BindingProperty:
|
||||
description: str
|
||||
default: object = None
|
||||
element_type: str = None
|
||||
min: object = None
|
||||
max: object = None
|
||||
|
||||
@dataclass
|
||||
class Binding:
|
||||
|
||||
@@ -76,6 +76,139 @@ def test_compile_invalid_dts():
|
||||
print("PASSED")
|
||||
return True
|
||||
|
||||
def write_minmax_config(tmp_dir, device_property_line, binding_min=0, binding_max=3, binding_default=1):
|
||||
config_dir = os.path.join(tmp_dir, "minmax_data")
|
||||
bindings_dir = os.path.join(config_dir, "bindings")
|
||||
os.makedirs(bindings_dir)
|
||||
|
||||
with open(os.path.join(config_dir, "devicetree.yaml"), "w") as f:
|
||||
f.write("dts: test.dts\nbindings: bindings")
|
||||
|
||||
with open(os.path.join(config_dir, "test.dts"), "w") as f:
|
||||
f.write(f"""/dts-v1/;
|
||||
|
||||
/ {{
|
||||
compatible = "test,root";
|
||||
model = "Test Model";
|
||||
|
||||
test-device@0 {{
|
||||
compatible = "test,minmax-device";
|
||||
{device_property_line}
|
||||
}};
|
||||
}};
|
||||
""")
|
||||
|
||||
with open(os.path.join(bindings_dir, "test,root.yaml"), "w") as f:
|
||||
f.write("description: Test root binding\ncompatible: \"test,root\"\nproperties:\n model:\n type: string\n")
|
||||
|
||||
with open(os.path.join(bindings_dir, "test,minmax-device.yaml"), "w") as f:
|
||||
f.write(f"""description: Test min/max binding
|
||||
compatible: "test,minmax-device"
|
||||
properties:
|
||||
ranged-prop:
|
||||
type: int
|
||||
default: {binding_default}
|
||||
min: {binding_min}
|
||||
max: {binding_max}
|
||||
""")
|
||||
|
||||
return config_dir
|
||||
|
||||
def test_minmax_within_range_succeeds():
|
||||
print("Running test_minmax_within_range_succeeds...")
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <2>;")
|
||||
output_dir = os.path.join(tmp_dir, "output")
|
||||
os.makedirs(output_dir)
|
||||
|
||||
result = run_compiler(config_dir, output_dir)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"FAILED: Compilation should have succeeded: {result.stderr} {result.stdout}")
|
||||
return False
|
||||
|
||||
print("PASSED")
|
||||
return True
|
||||
|
||||
def test_minmax_below_minimum_fails():
|
||||
print("Running test_minmax_below_minimum_fails...")
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <-1>;")
|
||||
output_dir = os.path.join(tmp_dir, "output")
|
||||
os.makedirs(output_dir)
|
||||
|
||||
result = run_compiler(config_dir, output_dir)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("FAILED: Compilation should have failed for a below-minimum value")
|
||||
return False
|
||||
|
||||
if "below minimum" not in result.stdout:
|
||||
print(f"FAILED: Expected 'below minimum' error message, got: {result.stdout}")
|
||||
return False
|
||||
|
||||
print("PASSED")
|
||||
return True
|
||||
|
||||
def test_minmax_above_maximum_fails():
|
||||
print("Running test_minmax_above_maximum_fails...")
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <7>;")
|
||||
output_dir = os.path.join(tmp_dir, "output")
|
||||
os.makedirs(output_dir)
|
||||
|
||||
result = run_compiler(config_dir, output_dir)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("FAILED: Compilation should have failed for an above-maximum value")
|
||||
return False
|
||||
|
||||
if "above maximum" not in result.stdout:
|
||||
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
|
||||
return False
|
||||
|
||||
print("PASSED")
|
||||
return True
|
||||
|
||||
def test_minmax_out_of_range_default_fails():
|
||||
print("Running test_minmax_out_of_range_default_fails...")
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# Property omitted from the .dts entirely, so the (invalid) binding default is used.
|
||||
config_dir = write_minmax_config(tmp_dir, "", binding_default=9)
|
||||
output_dir = os.path.join(tmp_dir, "output")
|
||||
os.makedirs(output_dir)
|
||||
|
||||
result = run_compiler(config_dir, output_dir)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("FAILED: Compilation should have failed for an out-of-range default value")
|
||||
return False
|
||||
|
||||
if "above maximum" not in result.stdout:
|
||||
print(f"FAILED: Expected 'above maximum' error message, got: {result.stdout}")
|
||||
return False
|
||||
|
||||
print("PASSED")
|
||||
return True
|
||||
|
||||
def test_minmax_symbolic_value_skips_validation():
|
||||
print("Running test_minmax_symbolic_value_skips_validation...")
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# A passed-through symbolic constant can't be range-checked at compile time and must
|
||||
# not be rejected just because a min/max is declared.
|
||||
config_dir = write_minmax_config(tmp_dir, "ranged-prop = <SOME_DEFINE>;")
|
||||
output_dir = os.path.join(tmp_dir, "output")
|
||||
os.makedirs(output_dir)
|
||||
|
||||
result = run_compiler(config_dir, output_dir)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"FAILED: Compilation should have succeeded for a symbolic value: {result.stderr} {result.stdout}")
|
||||
return False
|
||||
|
||||
print("PASSED")
|
||||
return True
|
||||
|
||||
def test_compile_missing_config():
|
||||
print("Running test_compile_missing_config...")
|
||||
with tempfile.TemporaryDirectory() as output_dir:
|
||||
@@ -96,7 +229,12 @@ if __name__ == "__main__":
|
||||
tests = [
|
||||
test_compile_success,
|
||||
test_compile_invalid_dts,
|
||||
test_compile_missing_config
|
||||
test_compile_missing_config,
|
||||
test_minmax_within_range_succeeds,
|
||||
test_minmax_below_minimum_fails,
|
||||
test_minmax_above_maximum_fails,
|
||||
test_minmax_out_of_range_default_fails,
|
||||
test_minmax_symbolic_value_skips_validation
|
||||
]
|
||||
|
||||
failed = 0
|
||||
|
||||
Reference in New Issue
Block a user