blob: 7d5c934c98945bb722e7c5cb2a1203dfd3f4640c [file] [log] [blame]
# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/assembly/assembled_system.gni")
import("//build/assembly/board_configuration.gni")
import("//build/assembly/generated_partitions_config.gni")
import("//build/assembly/package_list.gni")
import("//build/assembly/product_assembly_configuration.gni")
import("//build/assembly/update_package.gni")
import("//build/compiled_action.gni")
import("//build/components/fuchsia_package.gni")
import("//build/components/fuchsia_test_component.gni")
import("//build/config.gni")
import("//build/dist/resource.gni")
import("//build/dist/zip_resource.gni")
import("//build/info/info.gni")
import("//build/packages/publish-archive.gni")
import("//build/rust/rustc_binary.gni")
# Construct a system assembly for use in product security tests.
#
# Parameters:
#
#
# Assembly parameters:
#
# board_name (required):
# [string] board_name forwarded to assembled_system(target_name).
#
# base_packages (required):
# [list of labels] base_packages forwarded to assembled_system(target_name).
#
# system_version_file (required):
# [string] The file used to designate system version in the assembly's
# update package and build-info package.
#
# fvm_truncate_to_length (optional)
# [int] The precise size to make the (non-sparse) FVM image. See
# documentation of the `--length` parameter of the `fvm` binary host tool
# for details.
#
# use_fxfs_blob (optional; default: false)
# [bool] If set to true, prompts assembled_system to generate an fxfs image
# with fxblob and sets fshost's configuration to fxfs with fxfs_blob = true.
# If set to false, prompts assembled_system to generate an fvm and sets fshost's
# configuration to use minfs as the data format.
#
# Test asset packaging parameters:
#
# packaged_assembly_directory (required):
# [string] The subdirectory in data/assemblies where the
# packaged-in-a-fuchsia-package copy of assembled artifacts will be stored.
#
# update_domain (required):
# Domain name (that is, hostname of package server) used for OTA updates.
# There must be a corresponding `pkg-resolver` configuration for this
# domain, and update packages must designate this domain in their
# `packages.json` file.
#
# SSL/TLS asset packaging parameters:
#
# root_ssl_cert (required):
# [string] The gn-style path to a root SSL certificate that is compatible
# with SSL/TLS connections needed in the test environment. This must be
# configured to be compatible with certchain/server key pairs used by
# network-connected test components such as pkg_server.
#
# Public targets:
#
# ${target_name}:
# The assembled_system(target_name) { ... } used as a basis for other
# targets.
#
# ${target_name}_update_package:
# The update package for the assembled system.
#
# ${target_name}_system_resources:
# The resources required for serving the assembled system as installed on
# device. Resources reside in package's
# data/assemblies/${packaged_assembly_directory} directory.
#
# ${target_name}_update_package_resource:
# The update package as a resource stored in package's
# data/assemblies/${packaged_assembly_directory}/update/update.far.
#
# ${target_name}_tuf_repo_resources:
# The resources required for a static assets package server that can serve
# the assembled system (including the update package). Resources reside in
# package's data/assemblies/${packaged_assembly_directory}/repository
# directory.
template("assemble_security_pkg_test_system") {
assert(defined(invoker.board_name),
"board_name must be defined for $target_name")
assert(defined(invoker.base_packages),
"base_packages must be defined for $target_name")
assert(defined(invoker.system_version_file),
"system_version_file must be defined for $target_name")
assert(defined(invoker.packaged_assembly_directory),
"packaged_assembly_directory must be defined for $target_name")
assert(defined(invoker.update_domain),
"update_domain must be defined for $target_name")
assert(defined(invoker.root_ssl_cert),
"root_ssl_cert must be defined for $target_name")
assembly_name = target_name
if (current_toolchain == target_toolchain) {
packaged_assembly_directory = invoker.packaged_assembly_directory
} else {
not_needed(invoker, [ "packaged_assembly_directory" ])
}
use_fxfs_blob = false
if (defined(invoker.use_fxfs_blob)) {
use_fxfs_blob = invoker.use_fxfs_blob
}
labels = {
assembly = assembly_name
assembly_image_assembler = "${assembly_name}.image_assembler"
assembly_partitions_config = "${assembly_name}_partitions_config"
base_packages = "${assembly_name}.base_packages"
product_config = "${assembly_name}_product_config"
product_config_fxfs = "${assembly_name}_product_config_fxfs"
board_config = "${assembly_name}_board_config"
repository_config = "${assembly_name}_repository_config"
packages_json = "${assembly_name}_packages_json"
update_package = "${assembly_name}_update_package"
root_ssl_certificates = "${assembly_name}_root_ssl_certificates"
root_ssl_certificates_resource_group =
"${assembly_name}_root_ssl_certificates_resource_group"
system_resources = "${assembly_name}_system_resources"
update_package_resource = "${assembly_name}_update_package_resource"
tuf_repo_resources = "${assembly_name}_tuf_repo_resources"
base_package_manifests_list = "${assembly_name}.base_packages.list"
update_packages_manifests_lists =
"${assembly_name}.update_packages_manifests.list"
all_package_manifests_list = "${assembly_name}_all_package_manifests_list"
publish_archive = "${assembly_name}_publish_archive"
packages_for_update = "${assembly_name}_packages_for_update"
}
directories = {
assembly_out = get_label_info(":${labels.assembly}", "target_out_dir") +
"/${assembly_name}"
update_out = get_label_info(":${labels.assembly}", "target_out_dir") +
"/${labels.update_package}"
all_package_manifests_list_out =
get_label_info(":${labels.assembly}", "target_out_dir") +
"/${assembly_name}/all_package_manifests_list"
}
if (current_toolchain == target_toolchain) {
directories.packaged_repository =
"data/assemblies/${packaged_assembly_directory}/repository"
}
files = {
assembly_out_packages_json = "${directories.assembly_out}/packages.json"
packages_json =
"${directories.assembly_out}/security_pkg_test_packages.json"
partitions_json = "${directories.assembly_out}_partitions.json"
images_json = "${directories.assembly_out}/images.json"
board_config = "${directories.assembly_out}/board_config.json"
base_package_manifests_list =
"${target_out_dir}/${assembly_name}.base_packages.list"
update_packages_manifests_list =
"${target_out_dir}/${assembly_name}.update_packages.list"
# The combined set of base packages and the update package manifest(s) that
# lists them.
all_package_manifests_list = "${directories.all_package_manifests_list_out}/all_package_manifests.list"
}
# Resource files with both `local` and `packaged` paths.
if (current_toolchain == target_toolchain) {
# fxfs_blob is pulled in via assembled_system.gni which includes generated_fshost_config.gni.
# assembled_system will generate either a fxfs.blk or fvm.blk depending on the fxfs_blob flag.
if (use_fxfs_blob) {
fs_file_name = "fxfs"
} else {
fs_file_name = "fvm"
}
# Filesystem image can be fxfs or fvm, but the test should only need to know that it's
# a filesystem image. fshost will figure out which one it is when it's loaded into a VMO
# and added to the isolated dev tree via RamdiskClient.
fs_blk_resource = {
local = "${directories.assembly_out}/${fs_file_name}.blk"
packaged = "data/assemblies/${packaged_assembly_directory}/fs.blk"
}
base_far_resource = {
local = "${directories.assembly_out}/base/meta.far"
packaged = "data/assemblies/${packaged_assembly_directory}/base.far"
}
update_far_resource = {
local = "${directories.update_out}/update.far"
packaged =
"data/assemblies/${packaged_assembly_directory}/update/update.far"
}
root_ssl_certificates_resource = {
local = invoker.root_ssl_cert
packaged = "data/cert.pem"
}
} else {
# `invoker.root_ssl_cert` designates input only used for `target_toolchain`.
not_needed(invoker, [ "root_ssl_cert" ])
}
# Test-only root SSL certificates for domain names that may be used in tests.
fuchsia_package(labels.root_ssl_certificates) {
testonly = true
package_name = "root_ssl_certificates"
deps = [ ":${labels.root_ssl_certificates_resource_group}" ]
}
# Collect the build-info files using the default, but override the version.
# TODO(https://fxbug.dev/42169686): invoker should be able to override the product name
# to match the configured product.
_build_info = default_product_build_info
_build_info.version = rebase_path(invoker.system_version_file, root_build_dir)
product_assembly_configuration(labels.product_config) {
platform = {
build_type = "eng"
# We use a bootstrap assembly, because this is a test
# Even though we are building a fvm/fxfs, a bootstrap assembly cannot load
# blobs. The blobs are necessary for the test to exercise blob resolution.
# The test never tries to boot using this assembly, and uses the resolution
# code from the "main" assembly.
feature_set_level = "bootstrap"
storage = {
configure_fshost = true
filesystems = {
image_name = labels.assembly
volume = {
fvm = {
data = {
}
blob = {
}
}
}
}
}
}
product = {
build_info = _build_info
}
deps = [ "//build/info:build_info_files" ]
}
product_assembly_configuration(labels.product_config_fxfs) {
platform = {
build_type = "eng"
# We use a bootstrap assembly, because this is a test
# Even though we are building a fvm/fxfs, a bootstrap assembly cannot load
# blobs. The blobs are necessary for the test to exercise blob resolution.
# The test never tries to boot using this assembly, and uses the resolution
# code from the "main" assembly.
feature_set_level = "bootstrap"
storage = {
configure_fshost = true
filesystems = {
image_name = labels.assembly
}
}
}
product = {
build_info = _build_info
}
deps = [ "//build/info:build_info_files" ]
}
packages_for_assembly = [
":${labels.root_ssl_certificates}",
# TODO(https://fxbug.dev/42169686): There should be a contract for
# determining the correct configuration for every product.
# For now, this appears to be the only configuration in
# use.
"//src/sys/pkg/bin/system-update-committer:enable_reboot_on_verification_failure",
] + invoker.base_packages
board_configuration(labels.board_config) {
name = invoker.board_name
filesystems = {
fvm = {
slice_size = 1048576
sparse_output = {
}
}
fxfs = {
}
if (defined(invoker.fvm_truncate_to_length)) {
fvm.truncate_to_length = invoker.fvm_truncate_to_length
}
if (defined(invoker.assembly_fxfs_image_size_bytes)) {
fxfs.size_bytes = invoker.assembly_fxfs_image_size_bytes
}
}
}
# Main system assembly.
assembled_system(labels.assembly) {
testonly = true
generate_fvm = !use_fxfs_blob
generate_fxfs = use_fxfs_blob
use_fxfs_blob = use_fxfs_blob
if (!use_fxfs_blob) {
product_assembly_config_label = ":${labels.product_config}"
} else {
product_assembly_config_label = ":${labels.product_config_fxfs}"
}
board_config_label = ":${labels.board_config}"
use_bringup_platform_bundles_only = true
bootfs_labels = []
base_packages = packages_for_assembly
}
generated_partitions_config(labels.assembly_partitions_config) {
output_path = files.partitions_json
hw_revision = board_name
}
# Construct an update package for the system assembly.
update_package(labels.update_package) {
testonly = true
deps = [
":${labels.assembly_image_assembler}",
":${labels.assembly_partitions_config}",
]
partitions = files.partitions_json
system_a = files.images_json
board_name = board_name
version_file = invoker.system_version_file
epoch = "1"
if (defined(invoker.update_domain)) {
deps += [ ":${labels.assembly}" ]
rewrite_default_repo = invoker.update_domain
} else {
deps += [ ":${labels.packages_json}" ]
}
}
# Get the base packages from the assembled_system()
package_list_from_assembly(labels.base_package_manifests_list) {
testonly = true
system_label = ":${labels.assembly}"
package_set = "base"
contents = "manifest"
outputs = [ files.base_package_manifests_list ]
}
# Get the set of update packages (and image packages) via metadata
generate_package_metadata(labels.update_packages_manifests_lists) {
testonly = true
outputs = [ files.update_packages_manifests_list ]
data_keys = [ "package_output_manifests" ]
rebase = root_build_dir
deps = [ ":${labels.update_package}" ]
}
# Merge the list of base package manifests with the path to the update
# package's manifest to create a single list of package manifests.
action(labels.all_package_manifests_list) {
testonly = true
script = "//build/images/updates/create-all-package-manifests-list.py"
outputs = [ files.all_package_manifests_list ]
deps = [
":${labels.base_package_manifests_list}",
":${labels.update_packages_manifests_lists}",
]
args = [
rebase_path(outputs[0], root_build_dir),
rebase_path(files.base_package_manifests_list, root_build_dir),
rebase_path(files.update_packages_manifests_list, root_build_dir),
]
inputs = [
files.base_package_manifests_list,
files.update_packages_manifests_list,
]
}
# Publish TUF repository that corresponds to system update.
publish_archive(labels.publish_archive) {
testonly = true
deps = [ ":${labels.all_package_manifests_list}" ]
inputs = [ files.all_package_manifests_list ]
}
# `resource`/`resource_group` targets should be built on the
# `target_toolchain`. Other toolchains should depend on the `target_toolchain`
# outputs.
if (current_toolchain == target_toolchain) {
resource_group(labels.root_ssl_certificates_resource_group) {
testonly = true
files = [
{
source = root_ssl_certificates_resource.local
dest = root_ssl_certificates_resource.packaged
},
]
}
# Resources needed to host a system image of this assembly.
resource_group(labels.system_resources) {
testonly = true
deps = [ ":${labels.assembly}" ]
files = [
{
source = fs_blk_resource.local
dest = fs_blk_resource.packaged
},
{
source = base_far_resource.local
dest = base_far_resource.packaged
},
]
}
# Resource for update package far at a known path name.
resource_group(labels.update_package_resource) {
testonly = true
deps = [ ":${labels.update_package}" ]
files = [
{
source = update_far_resource.local
dest = update_far_resource.packaged
},
]
}
# Resources needed for serving an OTA update of this assembly.
zip_resource(labels.tuf_repo_resources) {
testonly = true
deps = [ ":${labels.publish_archive}" ]
publish_archive_outputs = get_target_outputs(":${labels.publish_archive}")
file = publish_archive_outputs[0]
dest_prefix = directories.packaged_repository
}
} else {
group(labels.root_ssl_certificates_resource_group) {
testonly = true
public_deps = [ ":${labels.root_ssl_certificates}($target_toolchain)" ]
}
group(labels.system_resources) {
testonly = true
public_deps = [ ":${labels.system_resources}($target_toolchain)" ]
}
group(labels.tuf_repo_resources) {
testonly = true
public_deps = [ ":${labels.tuf_repo_resources}($target_toolchain)" ]
}
group(labels.update_package_resource) {
testonly = true
public_deps = [ "${labels.update_package_resource}($target_toolchain)" ]
}
}
}