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