blob: 65a10673999a1978408d5b41fc9721cc0f5b80d8 [file] [log] [blame]
"""Functions related to designs.
See proto definitions for descriptions of arguments.
"""
load(
"@proto//chromiumos/config/api/design.proto",
design_pb = "chromiumos.config.api",
)
load(
"@proto//chromiumos/config/api/design_id.proto",
design_id_pb = "chromiumos.config.api",
)
load(
"@proto//chromiumos/config/api/software/software_config.proto",
sc_pb = "chromiumos.config.api.software",
)
# Needed to load from @proto. Add @unused to silence lint.
load("//config/util/bindings/proto.star", "protos")
load("//config/util/generate.star", "generate")
load("//config/util/hw_topology.star", "hw_topo")
load("//config/util/public_replication.star", "public_replication")
# Config identifier used for an unprovisioned configuration.
_UNPROVISIONED_CONFIG_ID = 0x7FFFFFFF
_CONSTRAINT = struct(
REQUIRED = design_pb.Design.Config.Constraint.REQUIRED,
PREFERRED = design_pb.Design.Config.Constraint.PREFERRED,
OPTIONAL = design_pb.Design.Config.Constraint.OPTIONAL,
)
_CUSTOMTYPE = struct(
NO_CUSTOM = design_pb.Design.NO_CUSTOM,
WHITELABEL = design_pb.Design.WHITELABEL,
REBRAND = design_pb.Design.REBRAND,
)
# Default hw_config_fields to be exposed
_DEFAULT_PUBLIC_HW_CONFIG_FIELDS = [
"id",
]
# Default sw_config_fields to be exposed
_DEFAULT_PUBLIC_SW_CONFIG_FIELDS = [
"design_config_id",
"id_scan_config",
]
_LAUNCHED_HW_PUBLIC_FIELDS = [
"hardware_features",
"hardware_topology.wifi.hardware_feature.fw_config",
]
_LAUNCHED_SW_PUBLIC_FIELDS = [
"bluetooth_config",
"camera_config",
"firmware_build_config",
"health_config",
"nnpalm_config",
"ui_config",
]
def _create_constraint(hw_features, level = _CONSTRAINT.REQUIRED):
"""Builds a Design.Config.Constraint proto."""
return design_pb.Design.Config.Constraint(level = level, features = hw_features)
def _create_constraints(hw_features, level = _CONSTRAINT.REQUIRED):
"""Builds a Design.Config.Constrain proto for each of hw_features."""
return [design_pb.Design.Config.Constraint(
level = level,
features = hw_feature,
) for hw_feature in hw_features]
def _append_configs(
sw_configs,
hw_configs,
design_id,
config_id,
extra_hw_config_public_fields = [],
extra_sw_config_public_fields = [],
hardware_topology = None,
firmware = None,
firmware_build_config = None,
firmware_info = None,
bluetooth = None,
power = None,
resource = None,
audio = None,
wifi = None,
camera = None,
health = None,
nnpalm = None,
ui = None,
usb = None,
rma = None,
device_tree_compatible_match = None,
smbios_name_match_override = None,
frid = None,
launched = False):
"""Creates and appends new SW and HW configs.
Create new Software and Hardware Design Configuration with the
specified properties and then append them to the sw_configs and hw_configs
arrays respectively. This ensures that all IDs are consistent.
Args:
sw_configs: An array to append the new SoftwareConfig to. Required.
hw_configs: An array to append the new Design.Config to. Required.
design_id: A DesignId to use for the Design.Config and SoftwareConfig.
Required.
config_id: A str or int used to construct the DesignConfigId for the
Design.Config and SoftwareConfig. Required.
extra_hw_config_public_fields: A list of str specifying fields on
Design.Config that will be made public in addition to the default
_DEFAULT_PUBLIC_HW_CONFIG_FIELDS. See PublicReplication proto
for details.
extra_sw_config_public_fields: A list of str specifying fields on
SoftwareConfig that will be made public in addition to the default
_DEFAULT_PUBLIC_SW_CONFIG_FIELDS. See PublicReplication proto for
details.
hardware_topology: A HardwareTopology to be used in the Design.Config.
firmware: A FirmwareConfig to be used in the SoftwareConfig.
firmware_build_config: A FirmwareBuildConfig to be used in the
SoftwareConfig.
firmware_info: Information related to runtime firmware,
bluetooth: A BluetoothConfig to be used in the SoftwareConfig.
power: A PowerConfig to be used in the SoftwareConfig.
resource: A ResourceConfig to be used in the SoftwareConfig.
audio: An AudioConfig to be used in the SoftwareConfig. Can be either a
single AudioConfig or a list of AudioConfigs.
wifi: A WifiConfig to be used in the SoftwareConfig.
camera: A CameraConfig to be used in the SoftwareConfig.
health: A HealthConfig to be used in the SoftwareConfig.
nnpalm: A NnpalmConfig to be used in the SoftwareConfig.
ui: A UiConfig to be used in the SoftwareConfig.
usb: UsbConfig to be used in the SoftwareConfig.
rma: An RmaConfig to be used in the SoftwareConfig.
device_tree_compatible_match: Deprecated, use FRID instead.
smbios_name_match_override: Deprecated, use FRID instead.
frid: String which must match the AP firmware FRID (first part before the
period) in order for the config to match. Leaving this value unset
will result in FRID being generated from coreboot target name or design ID.
launched: A bool indicating whether this config is launched, and as
such whether additional preset fields should be made public.
"""
# Ensure that config_id is convertable to int and is serialized as a
# decimal instead of a string. This makes it easier for a consumer
# to construct the DesignConfigId.value string correctly.
#
# This means that specifying
# config_id = "0x7fffffff"
# config_id = "0x7FFFFFFF"
# config_id = 0x7fffffff
#
# will all get serialized the same way, i.e. 2147483647.
config_id = int(config_id)
hw_config = design_pb.Design.Config()
hw_config.id.value = "%s:%s" % (design_id.value, config_id)
hw_config.hardware_topology = hardware_topology
hw_config.hardware_features = hw_topo.convert_to_hw_features(
hardware_topology,
)
hw_config_public_fields = list(_DEFAULT_PUBLIC_HW_CONFIG_FIELDS)
sw_config_public_fields = list(_DEFAULT_PUBLIC_SW_CONFIG_FIELDS)
if launched:
hw_config_public_fields += _LAUNCHED_HW_PUBLIC_FIELDS
sw_config_public_fields += _LAUNCHED_SW_PUBLIC_FIELDS
hw_config.public_replication = public_replication.create(
public_fields = hw_config_public_fields + extra_hw_config_public_fields,
)
hw_configs.append(hw_config)
sw_config = sc_pb.SoftwareConfig()
sw_config.design_config_id = hw_config.id
# Generate a default FRID from coreboot target name or design ID.
if not frid:
if firmware_build_config:
candidate_name = firmware_build_config.build_targets.coreboot
else:
candidate_name = design_id.value
frid = "Google_%s" % candidate_name.title()
sw_config.id_scan_config.frid = frid
# Ignoring all other options to generate FRID.
if device_tree_compatible_match:
print("WARNING: device_tree_compatible_match is deprecated. FRID `%s` is used for this config." % frid)
if smbios_name_match_override:
print("WARNING: smbios_name_match is deprecated. FRID `%s` is used for this config." % frid)
sw_config.id_scan_config.firmware_sku = config_id
sw_config.firmware = firmware
sw_config.firmware_build_config = firmware_build_config
sw_config.firmware_info = firmware_info
sw_config.bluetooth_config = bluetooth
sw_config.power_config = power
sw_config.resource_config = resource
if audio:
if type(audio) == "list":
sw_config.audio_configs.extend(audio)
else:
sw_config.audio_configs.append(audio)
sw_config.wifi_config = wifi
sw_config.camera_config = camera
sw_config.health_config = health
sw_config.nnpalm_config = nnpalm
sw_config.ui_config = ui
sw_config.usb_config = usb
sw_config.rma_config = rma
sw_config.public_replication = public_replication.create(
public_fields = sw_config_public_fields + extra_sw_config_public_fields,
)
sw_configs.append(sw_config)
def _create_design_id(name, config_design_id_override = None, model_name_design_id_override = None):
"""Builds a DesignId proto."""
return design_id_pb.DesignId(
value = name,
config_design_id_override = config_design_id_override,
model_name_design_id_override = model_name_design_id_override,
)
def _create_design(
id,
program_id,
odm_id,
public_fields = ["id", "name", "program_id"],
configs = None,
board_id_phases = None,
spi_flash_transform = None,
custom_type = _CUSTOMTYPE.NO_CUSTOM):
"""Builds a Design proto."""
return design_pb.Design(
id = id,
program_id = program_id,
odm_id = odm_id,
public_replication = public_replication.create(public_fields = public_fields),
name = id.value,
configs = configs,
board_id_phase = board_id_phases,
custom_type = custom_type,
spi_flash_transform = spi_flash_transform,
)
def _hoist_version(versioned_topologies, names):
return struct(
topology = dict(zip(names, [t.topology for t in versioned_topologies])),
version = max([t.version for t in versioned_topologies] + [0]),
)
def _cartesian_product(hardware_topology_bundle):
result = [[]]
for values in hardware_topology_bundle:
intermediate_result = []
for previous_value in result:
intermediate_result.extend([previous_value + [next_value] for next_value in values.topologies])
result = intermediate_result
names = [field.name for field in hardware_topology_bundle]
return [_hoist_version(value, names) for value in result]
def _find_unprovisioned_config(configs, unprovisioned_topologies):
for config in configs:
matches = True
for topology_type, topology in unprovisioned_topologies.items():
if config[topology_type] != topology:
matches = False
break
if matches:
return config
fail("Failed to find config matching unprovisioned_topologies: ", unprovisioned_topologies)
def _foreach_topology(
initial_config_id,
hardware_topology_bundle,
config_factory,
unprovisioned_topologies):
configs = [config.topology for config in sorted(
_cartesian_product(hardware_topology_bundle),
key = lambda config: config.version,
)]
unprovisioned_config = _find_unprovisioned_config(configs, unprovisioned_topologies)
config_factory(_UNPROVISIONED_CONFIG_ID, unprovisioned_config)
config_id = initial_config_id
for config in configs:
if config_factory(config_id, config):
config_id += 1
def _topology_name(topo):
if topo:
return topo.id
return "None"
def _create_design_with_configs(
design_id,
program_id,
odm_id,
sw_configs,
hardware_topology_bundle,
initial_config_id,
include_unprovisioned = False,
unprovisioned_topologies = {},
extra_hw_configs = None,
public_fields = ["id", "name", "program_id"],
board_id_phases = None,
custom_type = _CUSTOMTYPE.NO_CUSTOM,
extra_hw_config_public_fields = [],
extra_sw_config_public_fields = [],
firmware = None,
firmware_build_config = None,
firmware_info = None,
bluetooth = None,
power = None,
camera = None,
health = None,
ui = None,
frid = None,
hardware_topology_filter = None,
active_configs = None,
spi_flash_transform = None,
config_notes = {},
launched = False):
"""Create a design with configs for each topology combination in the bundle.
Parameters mirror those of design.append_configs() and design.create_design
(with id renamed to design_id, matching design.append_configs()). A config
is generated for each combination of topologies in
hardware_topology_bundle.
Args:
design_id: A DesignId to use for the Design.Config and SoftwareConfig.
program_id: ID that uniquely identifies the program.
odm_id: ODM for the given hardware design.
sw_configs: An array to append the new SoftwareConfig to.
hardware_topology_bundle: A bundle of hardware topology values created
by create_hardware_topology_bundle().
initial_config_id: The first design config ID to use. Consecutive IDs
will be used for the remaining configs, except those skipped due to
a hardware_topology_filter.
include_unprovisioned: Whether to generated a config for the
unprovisioned ID in addition to other configs. The same
configuration as used for initial_config_id will be used for the
unprovisioned config, unless unprovisioned_topologies is specified.
unprovisioned_topologies: A set of hardware topologies which the
unprovisioned config should incude. The unprovisioned config will be
set to the first generated config containing all of these topologies.
Implies include_unprovisioned.
extra_hw_configs: An array to append the extra Design.Config to.
public_fields: A list of strings specifying fields that should be made
public. See comment on the PublicReplication proto for semantics
and example of how the proto works.
board_id_phases: Board version assignment for each build phase.
custom_type: Define custom type as custom label or rebrand.
extra_hw_config_public_fields: A list of str specifying fields on
Design.Config that will be made public in addition to the default
_DEFAULT_PUBLIC_HW_CONFIG_FIELDS. See PublicReplication proto
for details.
extra_sw_config_public_fields: A list of str specifying fields on
SoftwareConfig that will be made public in addition to the default
_DEFAULT_PUBLIC_SW_CONFIG_FIELDS. See PublicReplication proto for
details.
firmware: A FirmwareConfig to be used in the SoftwareConfig.
firmware_build_config: A FirmwareBuildConfig to be used in the
SoftwareConfig.
firmware_info: Information related to runtime firmware,
bluetooth: A BluetoothConfig to be used in the SoftwareConfig.
power: A PowerConfig to be used in the SoftwareConfig.
camera: A CameraConfig to be used in the SoftwareConfig.
health: A HealthConfig to be used in the SoftwareConfig.
ui: A UiConfig to be used in the SoftwareConfig.
frid: String which must match the AP firmware FRID (first part before the
period) in order for the config to match. Leaving this value unset
will result in FRID being generated from coreboot target name or design ID.
hardware_topology_filter: An optional function filtering out the config ID and
the topologies to be used for that config. Return True to skip generating
this config.
active_configs: An array that contains the config IDs we need.
spi_flash_transform: An optional mapping of AP SPI flash chip names that
that provide a transform for the output of futility flash --get-info
to the input required by ap_wpsr tool. This supports the AP RO
verification features.
config_notes: Notes to document any particular DesignConfigId in the
generated markdown table.
launched: A bool indicating whether this design is launched, and as
such whether additional preset fields should be made public.
"""
hw_configs = []
if unprovisioned_topologies:
include_unprovisioned = True
if not active_configs:
active_configs = []
active_configs = list(active_configs)
active_configs += config_notes.keys()
if include_unprovisioned:
active_configs = [_UNPROVISIONED_CONFIG_ID] + active_configs
markdown = []
varying_topology_names = set(
[t.name for t in hardware_topology_bundle if len(t.topologies) > 1],
)
varying_topologies = []
def _is_active_config(config_id):
if config_id in active_configs:
return "v"
return ""
def config_factory(config_id, topologies):
if hardware_topology_filter:
filter_result = hardware_topology_filter(config_id = config_id, **topologies)
if filter_result:
return False
fw_config = 0
for topology in topologies.values():
if topology and proto.has(topology.hardware_feature, "fw_config"):
fw_config |= topology.hardware_feature.fw_config.value
varying_topologies.append([
"0x%x" % config_id,
"0x%x" % fw_config,
_is_active_config(config_id),
config_notes.get(config_id, ""),
] + [
_topology_name(t)
for name, t in sorted(topologies.items())
if name in varying_topology_names
] + ["`cbi set 2 0x%x 4`" % config_id, "`cbi set 6 0x%x 4`" % fw_config])
if config_id in active_configs:
print(design_id.value, "configs added: 0x%x" % config_id)
_append_configs(
hw_configs = hw_configs,
sw_configs = sw_configs,
design_id = design_id,
extra_hw_config_public_fields = extra_hw_config_public_fields,
extra_sw_config_public_fields = extra_sw_config_public_fields,
config_id = config_id,
hardware_topology = hw_topo.create_hardware_topology(**topologies),
firmware_build_config = firmware_build_config,
firmware = firmware,
firmware_info = firmware_info,
bluetooth = bluetooth,
power = power,
camera = camera,
health = health,
ui = ui,
frid = frid,
launched = launched,
)
return True
_foreach_topology(
initial_config_id = initial_config_id,
hardware_topology_bundle = hardware_topology_bundle,
config_factory = config_factory,
unprovisioned_topologies = unprovisioned_topologies,
)
markdown = [
["## Common topologies"],
["type", "name"],
["-"] * 2,
] + [
[t.name, _topology_name(t.topologies[0].topology)]
for t in hardware_topology_bundle
if len(t.topologies) == 1
] + [
[],
["## Varying topologies"],
["DesignConfigId", "fw_config", "active", "Notes"] + sorted(varying_topology_names) + ["CBI SKU_ID", "CBI FW_CONFIG"],
["-"] * (len(varying_topology_names) + 6),
] + varying_topologies + [[]]
designconfigid_table = "\n".join(["|".join(line) for line in markdown])
generate.gen_file(designconfigid_table, "_DesignConfigTable".join([design_id.value, ".md"]))
return _create_design(
id = design_id,
program_id = program_id,
odm_id = odm_id,
configs = hw_configs + (extra_hw_configs or []),
public_fields = public_fields,
board_id_phases = board_id_phases,
custom_type = custom_type,
spi_flash_transform = spi_flash_transform,
)
design = struct(
append_configs = _append_configs,
create_constraint = _create_constraint,
create_constraints = _create_constraints,
create_design_id = _create_design_id,
create_design = _create_design,
constraint = _CONSTRAINT,
custom_type = _CUSTOMTYPE,
generate = generate.generate,
UNPROVISIONED_CONFIG_ID = _UNPROVISIONED_CONFIG_ID,
create_design_with_configs = _create_design_with_configs,
)