diff options
| -rw-r--r-- | android/variable.go | 15 | ||||
| -rw-r--r-- | filesystem/android_device.go | 6 | ||||
| -rw-r--r-- | filesystem/vbmeta.go | 28 | ||||
| -rw-r--r-- | fsgen/Android.bp | 1 | ||||
| -rw-r--r-- | fsgen/filesystem_creator.go | 70 | ||||
| -rw-r--r-- | fsgen/fsgen_mutators.go | 1 | ||||
| -rw-r--r-- | fsgen/vbmeta_partitions.go | 174 |
7 files changed, 266 insertions, 29 deletions
diff --git a/android/variable.go b/android/variable.go index 142fab95f..f82c9cae8 100644 --- a/android/variable.go +++ b/android/variable.go @@ -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 @@ -601,7 +609,12 @@ type PartitionVariables struct { ProductUseDynamicPartitionSize bool `json:",omitempty"` CopyImagesForTargetFilesZip bool `json:",omitempty"` - BoardAvbEnable 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"` diff --git a/filesystem/android_device.go b/filesystem/android_device.go index 9071272c1..2645dc498 100644 --- a/filesystem/android_device.go +++ b/filesystem/android_device.go @@ -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) { diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go index 0bae479cf..6a3fc1f18 100644 --- a/filesystem/vbmeta.go +++ b/filesystem/vbmeta.go @@ -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 "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")" } - // Take the first line and remove the newline char - return "$(" + cmd + " | head -1 | tr -d '\n'" + ")" } // Extract public keys from chained_partitions.private_key. The keys are indexed with the partition diff --git a/fsgen/Android.bp b/fsgen/Android.bp index 690ad2821..8cd7518cb 100644 --- a/fsgen/Android.bp +++ b/fsgen/Android.bp @@ -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", diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go index bdffabf41..b580ea9b7 100644 --- a/fsgen/filesystem_creator.go +++ b/fsgen/filesystem_creator.go @@ -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...) } diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go index f3aec0015..92ea1282a 100644 --- a/fsgen/fsgen_mutators.go +++ b/fsgen/fsgen_mutators.go @@ -145,7 +145,6 @@ func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNam }, "system_dlkm": {}, }, - soongGeneratedPartitions: generatedPartitions(ctx), fsDepsMutex: sync.Mutex{}, moduleToInstallationProps: map[string]installationProperties{}, } diff --git a/fsgen/vbmeta_partitions.go b/fsgen/vbmeta_partitions.go new file mode 100644 index 000000000..f5b5f1cf0 --- /dev/null +++ b/fsgen/vbmeta_partitions.go @@ -0,0 +1,174 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fsgen + +import ( + "android/soong/android" + "android/soong/filesystem" + "slices" + "strconv" + + "github.com/google/blueprint/proptools" +) + +type vbmetaModuleInfo struct { + // The name of the generated vbmeta module + moduleName string + // The name of the module that avb understands. This is the name passed to --chain_partition, + // and also the basename of the output file. (the output file is called partitionName + ".img") + partitionName string +} + +// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names +// that the function created. May return nil if the product isn't using avb. +// +// 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. +func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo { + partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse + // Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false + if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable { + return nil + } + + var result []vbmetaModuleInfo + + var chainedPartitions []filesystem.ChainedPartitionProperties + var partitionTypesHandledByChainedPartitions []string + for chainedName, props := range partitionVars.ChainedVbmetaPartitions { + chainedName = "vbmeta_" + chainedName + if len(props.Partitions) == 0 { + continue + } + if len(props.Key) == 0 { + ctx.ModuleErrorf("No key found for chained avb partition %s", chainedName) + continue + } + if len(props.Algorithm) == 0 { + ctx.ModuleErrorf("No algorithm found for chained avb partition %s", chainedName) + continue + } + if len(props.RollbackIndex) == 0 { + ctx.ModuleErrorf("No rollback index found for chained avb partition %s", chainedName) + continue + } + ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32) + if err != nil { + ctx.ModuleErrorf("Rollback index location must be an int, got %s", props.RollbackIndexLocation) + continue + } + // The default is to use the PlatformSecurityPatch, and a lot of product config files + // just set it to the platform security patch, so detect that and don't set the property + // in soong. + var rollbackIndex *int64 + if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() { + i, err := strconv.ParseInt(props.RollbackIndex, 10, 32) + if err != nil { + ctx.ModuleErrorf("Rollback index must be an int, got %s", props.RollbackIndex) + continue + } + rollbackIndex = &i + } + + var partitionModules []string + for _, partition := range props.Partitions { + partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition) + if !slices.Contains(generatedPartitionTypes, partition) { + // The partition is probably unsupported. + continue + } + partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition)) + } + + name := generatedModuleName(ctx.Config(), chainedName) + ctx.CreateModuleInDirectory( + filesystem.VbmetaFactory, + ".", // Create in the root directory for now so its easy to get the key + &filesystem.VbmetaProperties{ + Partition_name: proptools.StringPtr(chainedName), + Stem: proptools.StringPtr(chainedName + ".img"), + Private_key: proptools.StringPtr(props.Key), + Algorithm: &props.Algorithm, + Rollback_index: rollbackIndex, + Rollback_index_location: &ril, + Partitions: proptools.NewSimpleConfigurable(partitionModules), + }, &struct { + Name *string + }{ + Name: &name, + }, + ).HideFromMake() + + chainedPartitions = append(chainedPartitions, filesystem.ChainedPartitionProperties{ + Name: &chainedName, + Rollback_index_location: &ril, + Private_key: &props.Key, + }) + + result = append(result, vbmetaModuleInfo{ + moduleName: name, + partitionName: chainedName, + }) + } + + vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta") + + var algorithm *string + if len(partitionVars.BoardAvbAlgorithm) > 0 { + algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm) + } + ril, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32) + if err != nil { + ctx.ModuleErrorf("Rollback index location must be an int, got %s", partitionVars.BoardAvbRollbackIndex) + } + + var partitionModules []string + for _, partitionType := range generatedPartitionTypes { + if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) { + // Already handled by a chained vbmeta partition + continue + } + partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType)) + } + + ctx.CreateModuleInDirectory( + filesystem.VbmetaFactory, + ".", // Create in the root directory for now so its easy to get the key + &filesystem.VbmetaProperties{ + Stem: proptools.StringPtr("vbmeta.img"), + Algorithm: algorithm, + Private_key: proptools.StringPtr(partitionVars.BoardAvbKeyPath), + Rollback_index_location: &ril, + Chained_partitions: chainedPartitions, + Partitions: proptools.NewSimpleConfigurable(partitionModules), + }, &struct { + Name *string + }{ + Name: &vbmetaModuleName, + }, + ).HideFromMake() + + result = append(result, vbmetaModuleInfo{ + moduleName: vbmetaModuleName, + partitionName: "vbmeta", + }) + return result +} |