• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Functions related to designs.
2
3See proto definitions for descriptions of arguments.
4"""
5
6load(
7    "@proto//chromiumos/config/api/design.proto",
8    design_pb = "chromiumos.config.api",
9)
10load(
11    "@proto//chromiumos/config/api/design_id.proto",
12    design_id_pb = "chromiumos.config.api",
13)
14load(
15    "@proto//chromiumos/config/api/software/software_config.proto",
16    sc_pb = "chromiumos.config.api.software",
17)
18
19# Needed to load from @proto. Add @unused to silence lint.
20load("//config/util/bindings/proto.star", "protos")
21load("//config/util/generate.star", "generate")
22load("//config/util/hw_topology.star", "hw_topo")
23load("//config/util/public_replication.star", "public_replication")
24
25# Config identifier used for an unprovisioned configuration.
26_UNPROVISIONED_CONFIG_ID = 0x7FFFFFFF
27
28_CONSTRAINT = struct(
29    REQUIRED = design_pb.Design.Config.Constraint.REQUIRED,
30    PREFERRED = design_pb.Design.Config.Constraint.PREFERRED,
31    OPTIONAL = design_pb.Design.Config.Constraint.OPTIONAL,
32)
33
34_CUSTOMTYPE = struct(
35    NO_CUSTOM = design_pb.Design.NO_CUSTOM,
36    WHITELABEL = design_pb.Design.WHITELABEL,
37    REBRAND = design_pb.Design.REBRAND,
38)
39
40# Default hw_config_fields to be exposed
41_DEFAULT_PUBLIC_HW_CONFIG_FIELDS = [
42    "id",
43]
44
45# Default sw_config_fields to be exposed
46_DEFAULT_PUBLIC_SW_CONFIG_FIELDS = [
47    "design_config_id",
48    "id_scan_config",
49]
50
51_LAUNCHED_HW_PUBLIC_FIELDS = [
52    "hardware_features",
53    "hardware_topology.wifi.hardware_feature.fw_config",
54]
55
56_LAUNCHED_SW_PUBLIC_FIELDS = [
57    "bluetooth_config",
58    "camera_config",
59    "firmware_build_config",
60    "health_config",
61    "nnpalm_config",
62    "ui_config",
63]
64
65def _create_constraint(hw_features, level = _CONSTRAINT.REQUIRED):
66    """Builds a Design.Config.Constraint proto."""
67    return design_pb.Design.Config.Constraint(level = level, features = hw_features)
68
69def _create_constraints(hw_features, level = _CONSTRAINT.REQUIRED):
70    """Builds a Design.Config.Constrain proto for each of hw_features."""
71    return [design_pb.Design.Config.Constraint(
72        level = level,
73        features = hw_feature,
74    ) for hw_feature in hw_features]
75
76def _append_configs(
77        sw_configs,
78        hw_configs,
79        design_id,
80        config_id,
81        extra_hw_config_public_fields = [],
82        extra_sw_config_public_fields = [],
83        hardware_topology = None,
84        firmware = None,
85        firmware_build_config = None,
86        firmware_info = None,
87        bluetooth = None,
88        power = None,
89        resource = None,
90        audio = None,
91        wifi = None,
92        camera = None,
93        disk_layout = None,
94        health = None,
95        nnpalm = None,
96        ui = None,
97        usb = None,
98        rma = None,
99        device_tree_compatible_match = None,
100        smbios_name_match_override = None,
101        frid = None,
102        launched = False):
103    """Creates and appends new SW and HW configs.
104
105    Create new Software and Hardware Design Configuration with the
106    specified properties and then append them to the sw_configs and hw_configs
107    arrays respectively. This ensures that all IDs are consistent.
108
109    Args:
110        sw_configs: An array to append the new SoftwareConfig to. Required.
111        hw_configs: An array to append the new Design.Config to. Required.
112        design_id: A DesignId to use for the Design.Config and SoftwareConfig.
113            Required.
114        config_id: A str or int used to construct the DesignConfigId for the
115            Design.Config and SoftwareConfig. Required.
116        extra_hw_config_public_fields: A list of str specifying fields on
117            Design.Config that will be made public in addition to the default
118            _DEFAULT_PUBLIC_HW_CONFIG_FIELDS. See PublicReplication proto
119            for details.
120        extra_sw_config_public_fields: A list of str specifying fields on
121            SoftwareConfig that will be made public in addition to the default
122            _DEFAULT_PUBLIC_SW_CONFIG_FIELDS. See PublicReplication proto for
123            details.
124        hardware_topology: A HardwareTopology to be used in the Design.Config.
125        firmware: A FirmwareConfig to be used in the SoftwareConfig.
126        firmware_build_config: A FirmwareBuildConfig to be used in the
127            SoftwareConfig.
128        firmware_info: Information related to runtime firmware,
129        bluetooth: A BluetoothConfig to be used in the SoftwareConfig.
130        power: A PowerConfig to be used in the SoftwareConfig.
131        resource: A ResourceConfig to be used in the SoftwareConfig.
132        audio: An AudioConfig to be used in the SoftwareConfig. Can be either a
133            single AudioConfig or a list of AudioConfigs.
134        wifi: A WifiConfig to be used in the SoftwareConfig.
135        camera: A CameraConfig to be used in the SoftwareConfig.
136        disk_layout: Define disk layout override, to be used in the SoftwareConfig.
137        health: A HealthConfig to be used in the SoftwareConfig.
138        nnpalm: A NnpalmConfig to be used in the SoftwareConfig.
139        ui: A UiConfig to be used in the SoftwareConfig.
140        usb: UsbConfig to be used in the SoftwareConfig.
141        rma: An RmaConfig to be used in the SoftwareConfig.
142        device_tree_compatible_match: Deprecated, use FRID instead.
143        smbios_name_match_override: Deprecated, use FRID instead.
144        frid: String which must match the AP firmware FRID (first part before the
145            period) in order for the config to match.  Leaving this value unset
146            will result in FRID being generated from coreboot target name or design ID.
147        launched: A bool indicating whether this config is launched, and as
148            such whether additional preset fields should be made public.
149    """
150
151    # Ensure that config_id is convertable to int and is serialized as a
152    # decimal instead of a string. This makes it easier for a consumer
153    # to construct the DesignConfigId.value string correctly.
154    #
155    # This means that specifying
156    #   config_id = "0x7fffffff"
157    #   config_id = "0x7FFFFFFF"
158    #   config_id = 0x7fffffff
159    #
160    # will all get serialized the same way, i.e. 2147483647.
161    config_id = int(config_id)
162
163    hw_config = design_pb.Design.Config()
164    hw_config.id.value = "%s:%s" % (design_id.value, config_id)
165    hw_config.hardware_topology = hardware_topology
166    hw_config.hardware_features = hw_topo.convert_to_hw_features(
167        hardware_topology,
168    )
169    hw_config_public_fields = list(_DEFAULT_PUBLIC_HW_CONFIG_FIELDS)
170    sw_config_public_fields = list(_DEFAULT_PUBLIC_SW_CONFIG_FIELDS)
171    if launched:
172        hw_config_public_fields += _LAUNCHED_HW_PUBLIC_FIELDS
173        sw_config_public_fields += _LAUNCHED_SW_PUBLIC_FIELDS
174    hw_config.public_replication = public_replication.create(
175        public_fields = hw_config_public_fields + extra_hw_config_public_fields,
176    )
177    hw_configs.append(hw_config)
178
179    sw_config = sc_pb.SoftwareConfig()
180    sw_config.design_config_id = hw_config.id
181
182    # Generate a default FRID from coreboot target name or design ID.
183    if not frid:
184        if firmware_build_config:
185            candidate_name = firmware_build_config.build_targets.coreboot
186        else:
187            candidate_name = design_id.value
188        frid = "Google_%s" % candidate_name.title()
189
190    sw_config.id_scan_config.frid = frid
191
192    # Ignoring all other options to generate FRID.
193    if device_tree_compatible_match:
194        print("WARNING: device_tree_compatible_match is deprecated. FRID `%s` is used for this config." % frid)
195
196    if smbios_name_match_override:
197        print("WARNING: smbios_name_match is deprecated. FRID `%s` is used for this config." % frid)
198
199    sw_config.id_scan_config.firmware_sku = config_id
200    sw_config.firmware = firmware
201    sw_config.firmware_build_config = firmware_build_config
202    sw_config.firmware_info = firmware_info
203    sw_config.bluetooth_config = bluetooth
204    sw_config.power_config = power
205    sw_config.resource_config = resource
206    if audio:
207        if type(audio) == "list":
208            sw_config.audio_configs.extend(audio)
209        else:
210            sw_config.audio_configs.append(audio)
211    sw_config.wifi_config = wifi
212    sw_config.camera_config = camera
213    sw_config.disk_layout = disk_layout
214    sw_config.health_config = health
215    sw_config.nnpalm_config = nnpalm
216    sw_config.ui_config = ui
217    sw_config.usb_config = usb
218    sw_config.rma_config = rma
219    sw_config.public_replication = public_replication.create(
220        public_fields = sw_config_public_fields + extra_sw_config_public_fields,
221    )
222    sw_configs.append(sw_config)
223
224def _create_design_id(name, config_design_id_override = None, model_name_design_id_override = None):
225    """Builds a DesignId proto."""
226    return design_id_pb.DesignId(
227        value = name,
228        config_design_id_override = config_design_id_override,
229        model_name_design_id_override = model_name_design_id_override,
230    )
231
232def _create_design(
233        id,
234        program_id,
235        odm_id,
236        public_fields = ["id", "name", "program_id"],
237        configs = None,
238        board_id_phases = None,
239        spi_flash_transform = None,
240        custom_type = _CUSTOMTYPE.NO_CUSTOM):
241    """Builds a Design proto."""
242    return design_pb.Design(
243        id = id,
244        program_id = program_id,
245        odm_id = odm_id,
246        public_replication = public_replication.create(public_fields = public_fields),
247        name = id.value,
248        configs = configs,
249        board_id_phase = board_id_phases,
250        custom_type = custom_type,
251        spi_flash_transform = spi_flash_transform,
252    )
253
254def _hoist_version(versioned_topologies, names):
255    return struct(
256        topology = dict(zip(names, [t.topology for t in versioned_topologies])),
257        version = max([t.version for t in versioned_topologies] + [0]),
258    )
259
260def _cartesian_product(hardware_topology_bundle):
261    result = [[]]
262    for values in hardware_topology_bundle:
263        intermediate_result = []
264        for previous_value in result:
265            intermediate_result.extend([previous_value + [next_value] for next_value in values.topologies])
266        result = intermediate_result
267
268    names = [field.name for field in hardware_topology_bundle]
269    return [_hoist_version(value, names) for value in result]
270
271def _find_unprovisioned_config(configs, unprovisioned_topologies):
272    for config in configs:
273        matches = True
274        for topology_type, topology in unprovisioned_topologies.items():
275            if config[topology_type] != topology:
276                matches = False
277                break
278        if matches:
279            return config
280
281    fail("Failed to find config matching unprovisioned_topologies: ", unprovisioned_topologies)
282
283def _foreach_topology(
284        initial_config_id,
285        hardware_topology_bundle,
286        config_factory,
287        unprovisioned_topologies):
288    configs = [config.topology for config in sorted(
289        _cartesian_product(hardware_topology_bundle),
290        key = lambda config: config.version,
291    )]
292
293    unprovisioned_config = _find_unprovisioned_config(configs, unprovisioned_topologies)
294    config_factory(_UNPROVISIONED_CONFIG_ID, unprovisioned_config)
295
296    config_id = initial_config_id
297    for config in configs:
298        if config_factory(config_id, config):
299            config_id += 1
300
301def _topology_name(topo):
302    if topo:
303        return topo.id
304    return "None"
305
306def _create_design_with_configs(
307        design_id,
308        program_id,
309        odm_id,
310        sw_configs,
311        hardware_topology_bundle,
312        initial_config_id,
313        include_unprovisioned = False,
314        unprovisioned_topologies = {},
315        extra_hw_configs = None,
316        public_fields = ["id", "name", "program_id"],
317        board_id_phases = None,
318        custom_type = _CUSTOMTYPE.NO_CUSTOM,
319        extra_hw_config_public_fields = [],
320        extra_sw_config_public_fields = [],
321        firmware = None,
322        firmware_build_config = None,
323        firmware_info = None,
324        bluetooth = None,
325        power = None,
326        camera = None,
327        disk_layout = None,
328        health = None,
329        ui = None,
330        frid = None,
331        rma = None,
332        hardware_topology_filter = None,
333        active_configs = None,
334        spi_flash_transform = None,
335        config_notes = {},
336        launched = False):
337    """Create a design with configs for each topology combination in the bundle.
338
339    Parameters mirror those of design.append_configs() and design.create_design
340    (with id renamed to design_id, matching design.append_configs()). A config
341    is generated for each combination of topologies in
342    hardware_topology_bundle.
343
344    Args:
345        design_id: A DesignId to use for the Design.Config and SoftwareConfig.
346        program_id: ID that uniquely identifies the program.
347        odm_id: ODM for the given hardware design.
348        sw_configs: An array to append the new SoftwareConfig to.
349        hardware_topology_bundle: A bundle of hardware topology values created
350            by create_hardware_topology_bundle().
351        initial_config_id: The first design config ID to use. Consecutive IDs
352            will be used for the remaining configs, except those skipped due to
353            a hardware_topology_filter.
354        include_unprovisioned: Whether to generated a config for the
355            unprovisioned ID in addition to other configs. The same
356            configuration as used for initial_config_id will be used for the
357            unprovisioned config, unless unprovisioned_topologies is specified.
358        unprovisioned_topologies: A set of hardware topologies which the
359            unprovisioned config should incude. The unprovisioned config will be
360            set to the first generated config containing all of these topologies.
361            Implies include_unprovisioned.
362        extra_hw_configs: An array to append the extra Design.Config to.
363        public_fields:  A list of strings specifying fields that should be made
364            public. See comment on the PublicReplication proto for semantics
365            and example of how the proto works.
366        board_id_phases: Board version assignment for each build phase.
367        custom_type: Define custom type as custom label or rebrand.
368        extra_hw_config_public_fields: A list of str specifying fields on
369            Design.Config that will be made public in addition to the default
370            _DEFAULT_PUBLIC_HW_CONFIG_FIELDS. See PublicReplication proto
371            for details.
372        extra_sw_config_public_fields: A list of str specifying fields on
373            SoftwareConfig that will be made public in addition to the default
374            _DEFAULT_PUBLIC_SW_CONFIG_FIELDS. See PublicReplication proto for
375            details.
376        firmware: A FirmwareConfig to be used in the SoftwareConfig.
377        firmware_build_config: A FirmwareBuildConfig to be used in the
378            SoftwareConfig.
379        firmware_info: Information related to runtime firmware,
380        bluetooth: A BluetoothConfig to be used in the SoftwareConfig.
381        power: A PowerConfig to be used in the SoftwareConfig.
382        disk_layout: Define disk layout override, to be used in the SoftwareConfig.
383        camera: A CameraConfig to be used in the SoftwareConfig.
384        health: A HealthConfig to be used in the SoftwareConfig.
385        ui: A UiConfig to be used in the SoftwareConfig.
386        frid: String which must match the AP firmware FRID (first part before the
387            period) in order for the config to match.  Leaving this value unset
388            will result in FRID being generated from coreboot target name or design ID.
389        rma: An RmaConfig to be used in the SoftwareConfig.
390        hardware_topology_filter: An optional function filtering out the config ID and
391            the topologies to be used for that config. Return True to skip generating
392            this config.
393        active_configs: An array that contains the config IDs we need.
394        spi_flash_transform: An optional mapping of AP SPI flash chip names that
395            that provide a transform for the output of futility flash --get-info
396            to the input required by ap_wpsr tool. This supports the AP RO
397            verification features.
398        config_notes: Notes to document any particular DesignConfigId in the
399            generated markdown table.
400        launched: A bool indicating whether this design is launched, and as
401            such whether additional preset fields should be made public.
402    """
403    hw_configs = []
404
405    if unprovisioned_topologies:
406        include_unprovisioned = True
407
408    if not active_configs:
409        active_configs = []
410    active_configs = list(active_configs)
411    active_configs += config_notes.keys()
412    if include_unprovisioned:
413        active_configs = [_UNPROVISIONED_CONFIG_ID] + active_configs
414
415    markdown = []
416    varying_topology_names = set(
417        [t.name for t in hardware_topology_bundle if len(t.topologies) > 1],
418    )
419    varying_topologies = []
420
421    def _is_active_config(config_id):
422        if config_id in active_configs:
423            return "v"
424        return ""
425
426    def config_factory(config_id, topologies):
427        if hardware_topology_filter:
428            filter_result = hardware_topology_filter(config_id = config_id, **topologies)
429            if filter_result:
430                return False
431
432        fw_config = 0
433        for topology in topologies.values():
434            if topology and proto.has(topology.hardware_feature, "fw_config"):
435                fw_config |= topology.hardware_feature.fw_config.value
436
437        varying_topologies.append([
438            "0x%x" % config_id,
439            "0x%x" % fw_config,
440            _is_active_config(config_id),
441            config_notes.get(config_id, ""),
442        ] + [
443            _topology_name(t)
444            for name, t in sorted(topologies.items())
445            if name in varying_topology_names
446        ] + ["`cbi set 2 0x%x 4`" % config_id, "`cbi set 6 0x%x 4`" % fw_config])
447
448        if config_id in active_configs:
449            print(design_id.value, "configs added: 0x%x" % config_id)
450
451            _append_configs(
452                hw_configs = hw_configs,
453                sw_configs = sw_configs,
454                design_id = design_id,
455                extra_hw_config_public_fields = extra_hw_config_public_fields,
456                extra_sw_config_public_fields = extra_sw_config_public_fields,
457                config_id = config_id,
458                hardware_topology = hw_topo.create_hardware_topology(**topologies),
459                firmware_build_config = firmware_build_config,
460                firmware = firmware,
461                firmware_info = firmware_info,
462                bluetooth = bluetooth,
463                power = power,
464                camera = camera,
465                disk_layout = disk_layout,
466                health = health,
467                ui = ui,
468                frid = frid,
469                rma = rma,
470                launched = launched,
471            )
472        return True
473
474    _foreach_topology(
475        initial_config_id = initial_config_id,
476        hardware_topology_bundle = hardware_topology_bundle,
477        config_factory = config_factory,
478        unprovisioned_topologies = unprovisioned_topologies,
479    )
480    markdown = [
481        ["## Common topologies"],
482        ["type", "name"],
483        ["-"] * 2,
484    ] + [
485        [t.name, _topology_name(t.topologies[0].topology)]
486        for t in hardware_topology_bundle
487        if len(t.topologies) == 1
488    ] + [
489        [],
490        ["## Varying topologies"],
491        ["DesignConfigId", "fw_config", "active", "Notes"] + sorted(varying_topology_names) + ["CBI SKU_ID", "CBI FW_CONFIG"],
492        ["-"] * (len(varying_topology_names) + 6),
493    ] + varying_topologies + [[]]
494    designconfigid_table = "\n".join(["|".join(line) for line in markdown])
495    generate.gen_file(designconfigid_table, "_DesignConfigTable".join([design_id.value, ".md"]))
496    return _create_design(
497        id = design_id,
498        program_id = program_id,
499        odm_id = odm_id,
500        configs = hw_configs + (extra_hw_configs or []),
501        public_fields = public_fields,
502        board_id_phases = board_id_phases,
503        custom_type = custom_type,
504        spi_flash_transform = spi_flash_transform,
505    )
506
507design = struct(
508    append_configs = _append_configs,
509    create_constraint = _create_constraint,
510    create_constraints = _create_constraints,
511    create_design_id = _create_design_id,
512    create_design = _create_design,
513    constraint = _CONSTRAINT,
514    custom_type = _CUSTOMTYPE,
515    generate = generate.generate,
516    UNPROVISIONED_CONFIG_ID = _UNPROVISIONED_CONFIG_ID,
517    create_design_with_configs = _create_design_with_configs,
518)
519