blob: a3d02603e5f517f218a53c9c33525f26eaec26f8 [file] [log] [blame]
# 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