Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fa1c6db authored by Cole Faust's avatar Cole Faust
Browse files

Build vbmeta partitions with soong

AVB is Android Verified Boot:
https://source.android.com/docs/security/features/verifiedboot
It works by signing all the partitions, but then also including an extra
metadata paritition called vbmeta that depends on all the other
signed partitions. This creates a requirement that you update all those
partitions and the vbmeta partition together, so in order to relax that
requirement products can set up "chained" vbmeta partitions, where a
chained partition like vbmeta_system might contain the avb metadata for
just a few products. In cuttlefish vbmeta_system contains metadata about
product, system, and system_ext. Using chained partitions, that group of
partitions can be updated independently from the other signed
partitions.

Bug: 377563298
Test: m nothing (the diff test currently fails, but this is a start)
Change-Id: I397af3a418bd23bcbf8f620c436afcacf69974fd
parent 084877aa
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -577,6 +577,14 @@ type PartitionQualifiedVariablesType struct {
	BoardAvbRollbackIndexLocation string `json:",omitempty"`
}

type ChainedAvbPartitionProps struct {
	Partitions            []string `json:",omitempty"`
	Key                   string   `json:",omitempty"`
	Algorithm             string   `json:",omitempty"`
	RollbackIndex         string   `json:",omitempty"`
	RollbackIndexLocation string   `json:",omitempty"`
}

type PartitionVariables struct {
	ProductDirectory            string `json:",omitempty"`
	PartitionQualifiedVariables map[string]PartitionQualifiedVariablesType
@@ -602,6 +610,11 @@ type PartitionVariables struct {
	CopyImagesForTargetFilesZip    bool   `json:",omitempty"`

	BoardAvbEnable          bool                                `json:",omitempty"`
	BoardAvbAlgorithm       string                              `json:",omitempty"`
	BoardAvbKeyPath         string                              `json:",omitempty"`
	BoardAvbRollbackIndex   string                              `json:",omitempty"`
	BuildingVbmetaImage     bool                                `json:",omitempty"`
	ChainedVbmetaPartitions map[string]ChainedAvbPartitionProps `json:",omitempty"`

	ProductPackages         []string `json:",omitempty"`
	ProductPackagesDebug    []string `json:",omitempty"`
+5 −1
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ type PartitionNameProperties struct {
	Vendor_partition_name *string
	// Name of the Odm partition filesystem module
	Odm_partition_name *string
	// The vbmeta partition and its "chained" partitions
	Vbmeta_partitions []string
}

type androidDevice struct {
@@ -46,7 +48,6 @@ func AndroidDeviceFactory() android.Module {
	module := &androidDevice{}
	module.AddProperties(&module.partitionProps)
	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)

	return module
}

@@ -69,6 +70,9 @@ func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) {
	addDependencyIfDefined(a.partitionProps.Product_partition_name)
	addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
	addDependencyIfDefined(a.partitionProps.Odm_partition_name)
	for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
		ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
	}
}

func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+12 −16
Original line number Diff line number Diff line
@@ -25,19 +25,19 @@ import (
)

func init() {
	android.RegisterModuleType("vbmeta", vbmetaFactory)
	android.RegisterModuleType("vbmeta", VbmetaFactory)
}

type vbmeta struct {
	android.ModuleBase

	properties vbmetaProperties
	properties VbmetaProperties

	output     android.OutputPath
	installDir android.InstallPath
}

type vbmetaProperties struct {
type VbmetaProperties struct {
	// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
	Partition_name *string

@@ -50,9 +50,8 @@ type vbmetaProperties struct {
	// Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096.
	Algorithm *string

	// File whose content will provide the rollback index. If unspecified, the rollback index
	// is from PLATFORM_SECURITY_PATCH
	Rollback_index_file *string `android:"path"`
	// The rollback index. If unspecified, the rollback index is from PLATFORM_SECURITY_PATCH
	Rollback_index *int64

	// Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0.
	Rollback_index_location *int64
@@ -62,7 +61,7 @@ type vbmetaProperties struct {
	Partitions proptools.Configurable[[]string]

	// List of chained partitions that this vbmeta deletages the verification.
	Chained_partitions []chainedPartitionProperties
	Chained_partitions []ChainedPartitionProperties

	// List of key-value pair of avb properties
	Avb_properties []avbProperty
@@ -76,7 +75,7 @@ type avbProperty struct {
	Value *string
}

type chainedPartitionProperties struct {
type ChainedPartitionProperties struct {
	// Name of the chained partition
	Name *string

@@ -95,7 +94,7 @@ type chainedPartitionProperties struct {
}

// vbmeta is the partition image that has the verification information for other partitions.
func vbmetaFactory() android.Module {
func VbmetaFactory() android.Module {
	module := &vbmeta{}
	module.AddProperties(&module.properties)
	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
@@ -217,15 +216,12 @@ func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) {

// Returns the embedded shell command that prints the rollback index
func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string {
	var cmd string
	if v.properties.Rollback_index_file != nil {
		f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file))
		cmd = "cat " + f.String()
	if v.properties.Rollback_index != nil {
		return fmt.Sprintf("%d", *v.properties.Rollback_index)
	} else {
		cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s"
	}
		// Take the first line and remove the newline char
	return "$(" + cmd + " | head -1 | tr -d '\n'" + ")"
		return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")"
	}
}

// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ bootstrap_go_package {
        "filesystem_creator.go",
        "fsgen_mutators.go",
        "prebuilt_etc_modules_gen.go",
        "vbmeta_partitions.go",
    ],
    testSrcs: [
        "filesystem_creator_test.go",
+60 −10
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ func registerBuildComponents(ctx android.RegistrationContext) {
type filesystemCreatorProps struct {
	Generated_partition_types   []string `blueprint:"mutated"`
	Unsupported_partition_types []string `blueprint:"mutated"`

	Vbmeta_module_names    []string `blueprint:"mutated"`
	Vbmeta_partition_names []string `blueprint:"mutated"`
}

type filesystemCreator struct {
@@ -67,16 +70,24 @@ func filesystemCreatorFactory() android.Module {
}

func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
	soongGeneratedPartitions := &ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions
	for _, partitionType := range *soongGeneratedPartitions {
	soongGeneratedPartitions := generatedPartitions(ctx)
	finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
	for _, partitionType := range soongGeneratedPartitions {
		if f.createPartition(ctx, partitionType) {
			f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
			finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
		} else {
			f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
			_, *soongGeneratedPartitions = android.RemoveFromList(partitionType, *soongGeneratedPartitions)
		}
	}
	f.createDeviceModule(ctx)

	for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
		f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
		f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
	}

	ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
	f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
}

func generatedModuleName(cfg android.Config, suffix string) string {
@@ -91,7 +102,11 @@ func generatedModuleNameForPartition(cfg android.Config, partitionType string) s
	return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
}

func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) {
func (f *filesystemCreator) createDeviceModule(
	ctx android.LoadHookContext,
	generatedPartitionTypes []string,
	vbmetaPartitions []string,
) {
	baseProps := &struct {
		Name *string
	}{
@@ -100,21 +115,22 @@ func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) {

	// Currently, only the system and system_ext partition module is created.
	partitionProps := &filesystem.PartitionNameProperties{}
	if android.InList("system", f.properties.Generated_partition_types) {
	if android.InList("system", generatedPartitionTypes) {
		partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
	}
	if android.InList("system_ext", f.properties.Generated_partition_types) {
	if android.InList("system_ext", generatedPartitionTypes) {
		partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
	}
	if android.InList("vendor", f.properties.Generated_partition_types) {
	if android.InList("vendor", generatedPartitionTypes) {
		partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
	}
	if android.InList("product", f.properties.Generated_partition_types) {
	if android.InList("product", generatedPartitionTypes) {
		partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
	}
	if android.InList("odm", f.properties.Generated_partition_types) {
	if android.InList("odm", generatedPartitionTypes) {
		partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
	}
	partitionProps.Vbmeta_partitions = vbmetaPartitions

	ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
}
@@ -334,12 +350,15 @@ func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadH
type filesystemBaseProperty struct {
	Name             *string
	Compile_multilib *string
	Visibility       []string
}

func generateBaseProps(namePtr *string) *filesystemBaseProperty {
	return &filesystemBaseProperty{
		Name:             namePtr,
		Compile_multilib: proptools.StringPtr("both"),
		// The vbmeta modules are currently in the root directory and depend on the partitions
		Visibility: []string{"//.", "//build/soong:__subpackages__"},
	}
}

@@ -435,16 +454,42 @@ func createFailingCommand(ctx android.ModuleContext, message string) android.Pat
	return file
}

func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
	vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
	outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
	if !ok {
		ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
	}
	if len(outputFilesProvider.DefaultOutputFiles) != 1 {
		ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
	}
	soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
	makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))

	diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
	builder := android.NewRuleBuilder(pctx, ctx)
	builder.Command().Text("diff").
		Input(soongVbMetaFile).
		Input(makeVbmetaFile)
	builder.Command().Text("touch").Output(diffTestResultFile)
	builder.Build(vbmetaModuleName+" diff test", vbmetaModuleName+" diff test")
	return diffTestResultFile
}

type systemImageDepTagType struct {
	blueprint.BaseDependencyTag
}

var generatedFilesystemDepTag systemImageDepTagType
var generatedVbmetaPartitionDepTag systemImageDepTagType

func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
	for _, partitionType := range f.properties.Generated_partition_types {
		ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
	}
	for _, vbmetaModule := range f.properties.Vbmeta_module_names {
		ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
	}
}

func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -474,6 +519,11 @@ func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContex
		diffTestFiles = append(diffTestFiles, diffTestFile)
		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
	}
	for i, vbmetaModule := range f.properties.Vbmeta_module_names {
		diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
		diffTestFiles = append(diffTestFiles, diffTestFile)
		ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
	}
	ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
}

Loading