| // 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) |
| pctx.HostBinToolVariable("avbtool", "avbtool") |
| } |
| |
| var ( |
| extractPublicKeyRule = pctx.AndroidStaticRule("avb_extract_public_key", |
| blueprint.RuleParams{ |
| Command: `${avbtool} extract_public_key --key $in --output $out`, |
| CommandDeps: []string{ |
| "${avbtool}", |
| }, |
| }) |
| ) |
| |
| type vbmeta struct { |
| android.ModuleBase |
| |
| properties VbmetaProperties |
| |
| output android.Path |
| 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 |
| |
| // 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 |
| |
| // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules |
| // have to be signed (use_avb: true). |
| Partitions proptools.Configurable[[]string] |
| |
| // Metadata about the chained partitions that this vbmeta delegates the verification. |
| // This is an alternative to chained_partitions, using chained_partitions instead is simpler |
| // in most cases. However, this property allows building this vbmeta partition without |
| // its chained partitions existing in this build. |
| Chained_partition_metadata []ChainedPartitionProperties |
| |
| // List of chained partitions that this vbmeta delegates the verification. They are the |
| // names of other vbmeta modules. |
| Chained_partitions []string |
| |
| // List of key-value pair of avb properties |
| Avb_properties []avbProperty |
| } |
| |
| type avbProperty struct { |
| // Key of given avb property |
| Key *string |
| |
| // Value of given avb property |
| Value *string |
| } |
| |
| 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"` |
| } |
| |
| type vbmetaPartitionInfo struct { |
| // Name of the partition |
| Name string |
| |
| // Rollback index location, non-negative int |
| RollbackIndexLocation int |
| |
| // The path to the public key of the private key used to sign this partition. Derived from |
| // the private key. |
| PublicKey android.Path |
| } |
| |
| var vbmetaPartitionProvider = blueprint.NewProvider[vbmetaPartitionInfo]() |
| |
| // 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.MultilibCommon) |
| return module |
| } |
| |
| type vbmetaDep struct { |
| blueprint.BaseDependencyTag |
| } |
| |
| type chainedPartitionDep struct { |
| blueprint.BaseDependencyTag |
| } |
| |
| var vbmetaPartitionDep = vbmetaDep{} |
| var vbmetaChainedPartitionDep = chainedPartitionDep{} |
| |
| func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) { |
| ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...) |
| ctx.AddDependency(ctx.Module(), vbmetaChainedPartitionDep, v.properties.Chained_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()) |
| } |
| |
| // See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE |
| const vbmetaMaxSize = 64 * 1024 |
| |
| func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { |
| 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 _, avb_prop := range v.properties.Avb_properties { |
| key := proptools.String(avb_prop.Key) |
| if key == "" { |
| ctx.PropertyErrorf("avb_properties", "key must be specified") |
| continue |
| } |
| value := proptools.String(avb_prop.Value) |
| if value == "" { |
| ctx.PropertyErrorf("avb_properties", "value must be specified") |
| continue |
| } |
| cmd.FlagWithArg("--prop ", key+":"+value) |
| } |
| |
| 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) |
| } |
| |
| seenRils := make(map[int]bool) |
| for _, cp := range ctx.GetDirectDepsWithTag(vbmetaChainedPartitionDep) { |
| info, ok := android.OtherModuleProvider(ctx, cp, vbmetaPartitionProvider) |
| if !ok { |
| ctx.PropertyErrorf("chained_partitions", "Expected all modules in chained_partitions to provide vbmetaPartitionProvider, but %s did not", cp.Name()) |
| continue |
| } |
| if info.Name == "" { |
| ctx.PropertyErrorf("chained_partitions", "name must be specified") |
| continue |
| } |
| |
| ril := info.RollbackIndexLocation |
| if ril < 0 { |
| ctx.PropertyErrorf("chained_partitions", "rollback index location must be 0, 1, 2, ...") |
| continue |
| } else if seenRils[ril] { |
| ctx.PropertyErrorf("chained_partitions", "Multiple chained partitions with the same rollback index location %d", ril) |
| continue |
| } |
| seenRils[ril] = true |
| |
| publicKey := info.PublicKey |
| cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", info.Name, ril, publicKey.String())) |
| cmd.Implicit(publicKey) |
| } |
| for _, cpm := range v.properties.Chained_partition_metadata { |
| name := proptools.String(cpm.Name) |
| if name == "" { |
| ctx.PropertyErrorf("chained_partitions", "name must be specified") |
| continue |
| } |
| |
| ril := proptools.IntDefault(cpm.Rollback_index_location, -1) |
| if ril < 0 { |
| ctx.PropertyErrorf("chained_partition_metadata", "rollback index location must be 0, 1, 2, ...") |
| continue |
| } else if seenRils[ril] { |
| ctx.PropertyErrorf("chained_partition_metadata", "Multiple chained partitions with the same rollback index location %d", ril) |
| continue |
| } |
| seenRils[ril] = true |
| |
| var publicKey android.Path |
| if cpm.Public_key != nil { |
| publicKey = android.PathForModuleSrc(ctx, *cpm.Public_key) |
| } else if cpm.Private_key != nil { |
| privateKey := android.PathForModuleSrc(ctx, *cpm.Private_key) |
| extractedPublicKey := android.PathForModuleOut(ctx, "chained_metadata", name+".avbpubkey") |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: extractPublicKeyRule, |
| Input: privateKey, |
| Output: extractedPublicKey, |
| }) |
| publicKey = extractedPublicKey |
| } else { |
| ctx.PropertyErrorf("public_key", "Either public_key or private_key must be specified") |
| continue |
| } |
| |
| cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String())) |
| cmd.Implicit(publicKey) |
| } |
| |
| output := android.PathForModuleOut(ctx, v.installFileName()) |
| cmd.FlagWithOutput("--output ", output) |
| |
| // libavb expects to be able to read the maximum vbmeta size, so we must provide a partition |
| // which matches this or the read will fail. |
| builder.Command().Text("truncate"). |
| FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)). |
| Output(output) |
| |
| builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName())) |
| |
| v.installDir = android.PathForModuleInstall(ctx, "etc") |
| ctx.InstallFile(v.installDir, v.installFileName(), output) |
| |
| extractedPublicKey := android.PathForModuleOut(ctx, v.partitionName()+".avbpubkey") |
| ctx.Build(pctx, android.BuildParams{ |
| Rule: extractPublicKeyRule, |
| Input: key, |
| Output: extractedPublicKey, |
| }) |
| |
| android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{ |
| Name: v.partitionName(), |
| RollbackIndexLocation: ril, |
| PublicKey: extractedPublicKey, |
| }) |
| |
| ctx.SetOutputFiles([]android.Path{output}, "") |
| v.output = output |
| } |
| |
| // Returns the embedded shell command that prints the rollback index |
| func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string { |
| if v.properties.Rollback_index != nil { |
| return fmt.Sprintf("%d", *v.properties.Rollback_index) |
| } else { |
| // Take the first line and remove the newline char |
| return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")" |
| } |
| } |
| |
| var _ android.AndroidMkProviderInfoProducer = (*vbmeta)(nil) |
| |
| func (v *vbmeta) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo { |
| providerData := android.AndroidMkProviderInfo{ |
| PrimaryInfo: android.AndroidMkInfo{ |
| Class: "ETC", |
| OutputFile: android.OptionalPathForPath(v.output), |
| EntryMap: make(map[string][]string), |
| }, |
| } |
| providerData.PrimaryInfo.SetString("LOCAL_MODULE_PATH", v.installDir.String()) |
| providerData.PrimaryInfo.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) |
| return &providerData |
| } |
| |
| 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 |
| } |