blob: 9fd5483daa1b6e85c32b52d6334edf812e29d130 [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
# Disable spurious no-name-in-module and import-error lints.
# pylint: disable=no-name-in-module, import-error
from checker import constraint_suite
from chromiumos.config.api import topology_pb2
from chromiumos.config.payload import config_bundle_pb2
from common import proto_utils
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
fw_cfg_segments = {
program.id.value: program.firmware_configuration_segments
for program in program_config.program_list
}
for segments in fw_cfg_segments.values():
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:
segments = fw_cfg_segments[design.program_id.value]
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