diff options
author | 2021-03-15 23:32:49 +0900 | |
---|---|---|
committer | 2021-03-16 11:34:11 +0900 | |
commit | 972e06c41f822750b8b8b62dfced90313f0eb6d4 (patch) | |
tree | 2a3826b2c91f30ca7587d950ca8946f94b0d2ae9 | |
parent | ac4076de9d5c517e144090487dc165538d15b0d2 (diff) |
Add vbmeta module type
The module type is to create vbmeta image out of other partitions.
Bug: 180676957
Test: m microdroid_vbmeta microdroid_vbmeta_system
Inspect the built image using `avbtool info_image --image <image>`
Change-Id: Iac92e9ab1640dcd488af69842e09850a91262bf1
-rw-r--r-- | filesystem/Android.bp | 1 | ||||
-rw-r--r-- | filesystem/bootimg.go | 7 | ||||
-rw-r--r-- | filesystem/filesystem.go | 11 | ||||
-rw-r--r-- | filesystem/logical_partition.go | 4 | ||||
-rw-r--r-- | filesystem/vbmeta.go | 265 |
5 files changed, 288 insertions, 0 deletions
diff --git a/filesystem/Android.bp b/filesystem/Android.bp index dcdbdcf43..791019ddb 100644 --- a/filesystem/Android.bp +++ b/filesystem/Android.bp @@ -14,6 +14,7 @@ bootstrap_go_package { "bootimg.go", "filesystem.go", "logical_partition.go", + "vbmeta.go", ], testSrcs: [ ], diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go index ce1c855d4..eb47e106a 100644 --- a/filesystem/bootimg.go +++ b/filesystem/bootimg.go @@ -263,6 +263,13 @@ func (b *bootimg) OutputPath() android.Path { return b.output } +func (b *bootimg) SignedOutputPath() android.Path { + if proptools.Bool(b.properties.Use_avb) { + return b.OutputPath() + } + return nil +} + var _ android.OutputFileProducer = (*bootimg)(nil) // Implements android.OutputFileProducer diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 7f36308df..8974eba4f 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -385,6 +385,10 @@ func (f *filesystem) OutputFiles(tag string) (android.Paths, error) { type Filesystem interface { android.Module OutputPath() android.Path + + // Returns the output file that is signed by avbtool. If this module is not signed, returns + // nil. + SignedOutputPath() android.Path } var _ Filesystem = (*filesystem)(nil) @@ -392,3 +396,10 @@ var _ Filesystem = (*filesystem)(nil) func (f *filesystem) OutputPath() android.Path { return f.output } + +func (f *filesystem) SignedOutputPath() android.Path { + if proptools.Bool(f.properties.Use_avb) { + return f.OutputPath() + } + return nil +} diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go index 16b6037cf..20d9622dc 100644 --- a/filesystem/logical_partition.go +++ b/filesystem/logical_partition.go @@ -209,6 +209,10 @@ func (l *logicalPartition) OutputPath() android.Path { return l.output } +func (l *logicalPartition) SignedOutputPath() android.Path { + return nil // logical partition is not signed by itself +} + var _ android.OutputFileProducer = (*logicalPartition)(nil) // Implements android.OutputFileProducer diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go new file mode 100644 index 000000000..f823387b0 --- /dev/null +++ b/filesystem/vbmeta.go @@ -0,0 +1,265 @@ +// Copyright (C) 2021 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 filesystem + +import ( + "fmt" + "strconv" + + "github.com/google/blueprint" + "github.com/google/blueprint/proptools" + + "android/soong/android" +) + +func init() { + android.RegisterModuleType("vbmeta", vbmetaFactory) +} + +type vbmeta struct { + android.ModuleBase + + properties vbmetaProperties + + output android.OutputPath + installDir android.InstallPath +} + +type vbmetaProperties struct { + // Name of the partition stored in vbmeta desc. Defaults to the name of this module. + Partition_name *string + + // Set the name of the output. Defaults to <module_name>.img. + Stem *string + + // Path to the private key that avbtool will use to sign this vbmeta image. + Private_key *string `android:"path"` + + // 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"` + + // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0. + Rollback_index_location *int64 + + // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules + // have to be signed (use_avb: true). + Partitions []string + + // List of chained partitions that this vbmeta deletages the verification. + Chained_partitions []chainedPartitionProperties +} + +type chainedPartitionProperties struct { + // Name of the chained partition + Name *string + + // Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the + // index of this partition in the list + 1. + Rollback_index_location *int64 + + // Path to the public key that the chained partition is signed with. If this is specified, + // private_key is ignored. + Public_key *string `android:"path"` + + // Path to the private key that the chained partition is signed with. If this is specified, + // and public_key is not specified, a public key is extracted from this private key and + // the extracted public key is embedded in the vbmeta image. + Private_key *string `android:"path"` +} + +// vbmeta is the partition image that has the verification information for other partitions. +func vbmetaFactory() android.Module { + module := &vbmeta{} + module.AddProperties(&module.properties) + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + +type vbmetaDep struct { + blueprint.BaseDependencyTag + kind string +} + +var vbmetaPartitionDep = vbmetaDep{kind: "partition"} + +func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) { + ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...) +} + +func (v *vbmeta) installFileName() string { + return proptools.StringDefault(v.properties.Stem, v.BaseModuleName()+".img") +} + +func (v *vbmeta) partitionName() string { + return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName()) +} + +func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { + extractedPublicKeys := v.extractPublicKeys(ctx) + + v.output = android.PathForModuleOut(ctx, v.installFileName()).OutputPath + + builder := android.NewRuleBuilder(pctx, ctx) + cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image") + + key := android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key)) + cmd.FlagWithInput("--key ", key) + + algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096") + cmd.FlagWithArg("--algorithm ", algorithm) + + cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx)) + ril := proptools.IntDefault(v.properties.Rollback_index_location, 0) + if ril < 0 { + ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...") + return + } + cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril)) + + for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) { + f, ok := p.(Filesystem) + if !ok { + ctx.PropertyErrorf("partitions", "%q(type: %s) is not supported", + p.Name(), ctx.OtherModuleType(p)) + continue + } + signedImage := f.SignedOutputPath() + if signedImage == nil { + ctx.PropertyErrorf("partitions", "%q(type: %s) is not signed. Use `use_avb: true`", + p.Name(), ctx.OtherModuleType(p)) + continue + } + cmd.FlagWithInput("--include_descriptors_from_image ", signedImage) + } + + for i, cp := range v.properties.Chained_partitions { + name := proptools.String(cp.Name) + if name == "" { + ctx.PropertyErrorf("chained_partitions", "name must be specified") + continue + } + + ril := proptools.IntDefault(cp.Rollback_index_location, i+1) + if ril < 0 { + ctx.PropertyErrorf("chained_partitions", "must be 0, 1, 2, ...") + continue + } + + var publicKey android.Path + if cp.Public_key != nil { + publicKey = android.PathForModuleSrc(ctx, proptools.String(cp.Public_key)) + } else { + publicKey = extractedPublicKeys[name] + } + cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String())) + cmd.Implicit(publicKey) + } + + cmd.FlagWithOutput("--output ", v.output) + builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName())) + + v.installDir = android.PathForModuleInstall(ctx, "etc") + ctx.InstallFile(v.installDir, v.installFileName(), v.output) +} + +// 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() + } 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'" + ")" +} + +// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition +// name. +func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android.OutputPath { + result := make(map[string]android.OutputPath) + + builder := android.NewRuleBuilder(pctx, ctx) + for _, cp := range v.properties.Chained_partitions { + if cp.Private_key == nil { + continue + } + + name := proptools.String(cp.Name) + if name == "" { + ctx.PropertyErrorf("chained_partitions", "name must be specified") + continue + } + + if _, ok := result[name]; ok { + ctx.PropertyErrorf("chained_partitions", "name %q is duplicated", name) + continue + } + + privateKeyFile := android.PathForModuleSrc(ctx, proptools.String(cp.Private_key)) + publicKeyFile := android.PathForModuleOut(ctx, name+".avbpubkey").OutputPath + + builder.Command(). + BuiltTool("avbtool"). + Text("extract_public_key"). + FlagWithInput("--key ", privateKeyFile). + FlagWithOutput("--output ", publicKeyFile) + + result[name] = publicKeyFile + } + builder.Build("vbmeta_extract_public_key", fmt.Sprintf("Extract public keys for %s", ctx.ModuleName())) + return result +} + +var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil) + +// Implements android.AndroidMkEntriesProvider +func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{android.AndroidMkEntries{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(v.output), + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", v.installDir.ToMakePath().String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) + }, + }, + }} +} + +var _ Filesystem = (*vbmeta)(nil) + +func (v *vbmeta) OutputPath() android.Path { + return v.output +} + +func (v *vbmeta) SignedOutputPath() android.Path { + return v.OutputPath() // vbmeta is always signed +} + +var _ android.OutputFileProducer = (*vbmeta)(nil) + +// Implements android.OutputFileProducer +func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) { + if tag == "" { + return []android.Path{v.output}, nil + } + return nil, fmt.Errorf("unsupported module reference tag %q", tag) +} |