| # Copyright 2020 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Constraint checks related to firmware configuration.""" |
| |
| import itertools |
| import pathlib |
| |
| from checker import config_bundle_utils |
| from checker import constraint_suite |
| from common import proto_utils |
| |
| from chromiumos.config.payload import config_bundle_pb2 |
| from chromiumos.config.api import topology_pb2 |
| |
| |
| def _topo_to_string(topo): |
| return '{}:{}'.format(topology_pb2.Topology.Type.Name(topo.type), topo.id) |
| |
| |
| class FirmwareConfigurationConstraintSuite(constraint_suite.ConstraintSuite): |
| """Constraint checks related to firmware configuration.""" |
| |
| def check_firmware_configuration_masks( |
| self, |
| program_config: config_bundle_pb2.ConfigBundle, |
| project_config: config_bundle_pb2.ConfigBundle, |
| factory_dir: pathlib.Path, |
| ): |
| """Checks firmware configuration masks are valid. |
| |
| 1. Check that the FirmwareConfigurationSegments defined in the program do |
| not overlap. |
| 2. Check that each mask defined in a FirmwareConfiguration aligns with a |
| segment. |
| """ |
| del factory_dir |
| |
| segments = config_bundle_utils.get_program( |
| program_config).firmware_configuration_segments |
| |
| for segment_a, segment_b in itertools.combinations(segments, 2): |
| overlap = segment_a.mask & segment_b.mask |
| self.assertFalse( |
| overlap, |
| msg='Overlap in masks {} and {}: {:b} & {:b} = {:b}'.format( |
| segment_a.name, segment_b.name, segment_a.mask, segment_b.mask, |
| overlap)) |
| |
| # For every topology that defines a FirmwareConfiguration, check the mask |
| # aligns with a segment. |
| for design in project_config.design_list: |
| for config in design.configs: |
| for topology in proto_utils.get_all_fields(config.hardware_topology): |
| mask = topology.hardware_feature.fw_config.mask |
| for seg in segments: |
| overlap = mask & seg.mask |
| if overlap: |
| self.assertEqual( |
| overlap, seg.mask, |
| 'Topology {} with fw_config mask 0x{:08X} did not specify the ' |
| 'complete fw_config field "{}" with mask 0x{:08X}'.format( |
| _topo_to_string(topology), |
| topology.hardware_feature.fw_config.mask, seg.name, |
| seg.mask)) |
| # Remove the valid seg.mask to keep track of any extra mask in |
| # the topology value |
| mask -= overlap |
| # After looping through all valid fw_config masks, ensure that topo's |
| # value is empty |
| self.assertEqual( |
| mask, 0, |
| 'Topology {} specifies fw_mask that is not known 0x{:08X}'.format( |
| _topo_to_string(topology), mask)) |
| |
| def check_firmware_configuration_value_collision( |
| self, |
| program_config: config_bundle_pb2.ConfigBundle, |
| project_config: config_bundle_pb2.ConfigBundle, |
| factory_dir: pathlib.Path, |
| ): |
| """Checks that a given firmware value is only used by a single topology. |
| |
| More precisely: For a given project, each FirmwareConfiguration.value is |
| used by exactly one (Topology.id, Topology.type) pair. |
| |
| For example, the following is a violation: |
| |
| thermal: < |
| id: "DEFAULT_THERMAL" |
| type: THERMAL |
| hardware_feature: < |
| fw_config: < |
| value: 11 |
| > |
| > |
| > |
| screen: < |
| id: "DEFAULT_SCREEN" |
| type: SCREEN |
| hardware_feature: < |
| fw_config: < |
| value: 11 |
| > |
| > |
| > |
| |
| because both ("DEFAULT_THERMAL", THERMAL) and ("DEFAULT_SCREEN", SCREEN) use |
| value 11. |
| """ |
| del program_config, factory_dir |
| |
| # Map from FirmwareConfiguration.value -> (Topology.id, Topology.type). |
| value_to_topo = {} |
| |
| for design in project_config.design_list: |
| for config in design.configs: |
| for topology in proto_utils.get_all_fields(config.hardware_topology): |
| fw_value = topology.hardware_feature.fw_config.value |
| if not fw_value: |
| continue |
| |
| # Topologies must set id and type. |
| self.assertTrue(topology.id) |
| self.assertTrue(topology.type) |
| topo_key = (topology.id, topology.type) |
| |
| prev_topo_key = value_to_topo.get(fw_value) |
| if prev_topo_key: |
| # We only need to ensure that the types are the same. Two different |
| # types cannot both set/control the same FW_CONFIG field value. |
| # It is allowed for two topology values of the same type to control |
| # the same FW_CONFIG field value (thus having the same FW_CONFIG |
| # value). |
| self.assertEqual( |
| topo_key[1], |
| prev_topo_key[1], |
| msg=('Topologies ({id1}, {type1}) and ({id2}, {type2}) both use' |
| ' firmware value {fw_value}' |
| ' but have different types').format( |
| id1=topo_key[0], |
| type1=topology_pb2.Topology.Type.Name(topo_key[1]), |
| id2=prev_topo_key[0], |
| type2=topology_pb2.Topology.Type.Name( |
| prev_topo_key[1]), |
| fw_value=fw_value)) |
| else: |
| value_to_topo[fw_value] = topo_key |