1// Copyright (C) 2021 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package filesystem 16 17import ( 18 "fmt" 19 "strconv" 20 21 "github.com/google/blueprint" 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25) 26 27func init() { 28 android.RegisterModuleType("vbmeta", VbmetaFactory) 29 pctx.HostBinToolVariable("avbtool", "avbtool") 30} 31 32var ( 33 extractPublicKeyRule = pctx.AndroidStaticRule("avb_extract_public_key", 34 blueprint.RuleParams{ 35 Command: `${avbtool} extract_public_key --key $in --output $out`, 36 CommandDeps: []string{ 37 "${avbtool}", 38 }, 39 }) 40) 41 42type vbmeta struct { 43 android.ModuleBase 44 45 properties VbmetaProperties 46 47 output android.Path 48 installDir android.InstallPath 49} 50 51type VbmetaProperties struct { 52 // Name of the partition stored in vbmeta desc. Defaults to the name of this module. 53 Partition_name *string 54 55 // Set the name of the output. Defaults to <module_name>.img. 56 Stem *string 57 58 // Path to the private key that avbtool will use to sign this vbmeta image. 59 Private_key *string `android:"path"` 60 61 // Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096. 62 Algorithm *string 63 64 // The rollback index. If unspecified, the rollback index is from PLATFORM_SECURITY_PATCH 65 Rollback_index *int64 66 67 // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0. 68 Rollback_index_location *int64 69 70 // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules 71 // have to be signed (use_avb: true). 72 Partitions proptools.Configurable[[]string] 73 74 // Metadata about the chained partitions that this vbmeta delegates the verification. 75 // This is an alternative to chained_partitions, using chained_partitions instead is simpler 76 // in most cases. However, this property allows building this vbmeta partition without 77 // its chained partitions existing in this build. 78 Chained_partition_metadata []ChainedPartitionProperties 79 80 // List of chained partitions that this vbmeta delegates the verification. They are the 81 // names of other vbmeta modules. 82 Chained_partitions []string 83 84 // List of key-value pair of avb properties 85 Avb_properties []avbProperty 86} 87 88type avbProperty struct { 89 // Key of given avb property 90 Key *string 91 92 // Value of given avb property 93 Value *string 94} 95 96type ChainedPartitionProperties struct { 97 // Name of the chained partition 98 Name *string 99 100 // Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the 101 // index of this partition in the list + 1. 102 Rollback_index_location *int64 103 104 // Path to the public key that the chained partition is signed with. If this is specified, 105 // private_key is ignored. 106 Public_key *string `android:"path"` 107 108 // Path to the private key that the chained partition is signed with. If this is specified, 109 // and public_key is not specified, a public key is extracted from this private key and 110 // the extracted public key is embedded in the vbmeta image. 111 Private_key *string `android:"path"` 112} 113 114type vbmetaPartitionInfo struct { 115 // Name of the partition 116 Name string 117 118 // Rollback index location, non-negative int 119 RollbackIndexLocation int 120 121 // The path to the public key of the private key used to sign this partition. Derived from 122 // the private key. 123 PublicKey android.Path 124} 125 126var vbmetaPartitionProvider = blueprint.NewProvider[vbmetaPartitionInfo]() 127 128// vbmeta is the partition image that has the verification information for other partitions. 129func VbmetaFactory() android.Module { 130 module := &vbmeta{} 131 module.AddProperties(&module.properties) 132 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) 133 return module 134} 135 136type vbmetaDep struct { 137 blueprint.BaseDependencyTag 138} 139 140type chainedPartitionDep struct { 141 blueprint.BaseDependencyTag 142} 143 144var vbmetaPartitionDep = vbmetaDep{} 145var vbmetaChainedPartitionDep = chainedPartitionDep{} 146 147func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) { 148 ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...) 149 ctx.AddDependency(ctx.Module(), vbmetaChainedPartitionDep, v.properties.Chained_partitions...) 150} 151 152func (v *vbmeta) installFileName() string { 153 return proptools.StringDefault(v.properties.Stem, v.BaseModuleName()+".img") 154} 155 156func (v *vbmeta) partitionName() string { 157 return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName()) 158} 159 160// See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE 161const vbmetaMaxSize = 64 * 1024 162 163func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { 164 builder := android.NewRuleBuilder(pctx, ctx) 165 cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image") 166 167 key := android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key)) 168 cmd.FlagWithInput("--key ", key) 169 170 algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096") 171 cmd.FlagWithArg("--algorithm ", algorithm) 172 173 cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx)) 174 ril := proptools.IntDefault(v.properties.Rollback_index_location, 0) 175 if ril < 0 { 176 ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...") 177 return 178 } 179 cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril)) 180 181 for _, avb_prop := range v.properties.Avb_properties { 182 key := proptools.String(avb_prop.Key) 183 if key == "" { 184 ctx.PropertyErrorf("avb_properties", "key must be specified") 185 continue 186 } 187 value := proptools.String(avb_prop.Value) 188 if value == "" { 189 ctx.PropertyErrorf("avb_properties", "value must be specified") 190 continue 191 } 192 cmd.FlagWithArg("--prop ", key+":"+value) 193 } 194 195 for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) { 196 f, ok := p.(Filesystem) 197 if !ok { 198 ctx.PropertyErrorf("partitions", "%q(type: %s) is not supported", 199 p.Name(), ctx.OtherModuleType(p)) 200 continue 201 } 202 signedImage := f.SignedOutputPath() 203 if signedImage == nil { 204 ctx.PropertyErrorf("partitions", "%q(type: %s) is not signed. Use `use_avb: true`", 205 p.Name(), ctx.OtherModuleType(p)) 206 continue 207 } 208 cmd.FlagWithInput("--include_descriptors_from_image ", signedImage) 209 } 210 211 seenRils := make(map[int]bool) 212 for _, cp := range ctx.GetDirectDepsWithTag(vbmetaChainedPartitionDep) { 213 info, ok := android.OtherModuleProvider(ctx, cp, vbmetaPartitionProvider) 214 if !ok { 215 ctx.PropertyErrorf("chained_partitions", "Expected all modules in chained_partitions to provide vbmetaPartitionProvider, but %s did not", cp.Name()) 216 continue 217 } 218 if info.Name == "" { 219 ctx.PropertyErrorf("chained_partitions", "name must be specified") 220 continue 221 } 222 223 ril := info.RollbackIndexLocation 224 if ril < 0 { 225 ctx.PropertyErrorf("chained_partitions", "rollback index location must be 0, 1, 2, ...") 226 continue 227 } else if seenRils[ril] { 228 ctx.PropertyErrorf("chained_partitions", "Multiple chained partitions with the same rollback index location %d", ril) 229 continue 230 } 231 seenRils[ril] = true 232 233 publicKey := info.PublicKey 234 cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", info.Name, ril, publicKey.String())) 235 cmd.Implicit(publicKey) 236 } 237 for _, cpm := range v.properties.Chained_partition_metadata { 238 name := proptools.String(cpm.Name) 239 if name == "" { 240 ctx.PropertyErrorf("chained_partitions", "name must be specified") 241 continue 242 } 243 244 ril := proptools.IntDefault(cpm.Rollback_index_location, -1) 245 if ril < 0 { 246 ctx.PropertyErrorf("chained_partition_metadata", "rollback index location must be 0, 1, 2, ...") 247 continue 248 } else if seenRils[ril] { 249 ctx.PropertyErrorf("chained_partition_metadata", "Multiple chained partitions with the same rollback index location %d", ril) 250 continue 251 } 252 seenRils[ril] = true 253 254 var publicKey android.Path 255 if cpm.Public_key != nil { 256 publicKey = android.PathForModuleSrc(ctx, *cpm.Public_key) 257 } else if cpm.Private_key != nil { 258 privateKey := android.PathForModuleSrc(ctx, *cpm.Private_key) 259 extractedPublicKey := android.PathForModuleOut(ctx, "chained_metadata", name+".avbpubkey") 260 ctx.Build(pctx, android.BuildParams{ 261 Rule: extractPublicKeyRule, 262 Input: privateKey, 263 Output: extractedPublicKey, 264 }) 265 publicKey = extractedPublicKey 266 } else { 267 ctx.PropertyErrorf("public_key", "Either public_key or private_key must be specified") 268 continue 269 } 270 271 cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String())) 272 cmd.Implicit(publicKey) 273 } 274 275 output := android.PathForModuleOut(ctx, v.installFileName()) 276 cmd.FlagWithOutput("--output ", output) 277 278 // libavb expects to be able to read the maximum vbmeta size, so we must provide a partition 279 // which matches this or the read will fail. 280 builder.Command().Text("truncate"). 281 FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)). 282 Output(output) 283 284 builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName())) 285 286 v.installDir = android.PathForModuleInstall(ctx, "etc") 287 ctx.InstallFile(v.installDir, v.installFileName(), output) 288 289 extractedPublicKey := android.PathForModuleOut(ctx, v.partitionName()+".avbpubkey") 290 ctx.Build(pctx, android.BuildParams{ 291 Rule: extractPublicKeyRule, 292 Input: key, 293 Output: extractedPublicKey, 294 }) 295 296 android.SetProvider(ctx, vbmetaPartitionProvider, vbmetaPartitionInfo{ 297 Name: v.partitionName(), 298 RollbackIndexLocation: ril, 299 PublicKey: extractedPublicKey, 300 }) 301 302 ctx.SetOutputFiles([]android.Path{output}, "") 303 v.output = output 304} 305 306// Returns the embedded shell command that prints the rollback index 307func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string { 308 if v.properties.Rollback_index != nil { 309 return fmt.Sprintf("%d", *v.properties.Rollback_index) 310 } else { 311 // Take the first line and remove the newline char 312 return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")" 313 } 314} 315 316var _ android.AndroidMkProviderInfoProducer = (*vbmeta)(nil) 317 318func (v *vbmeta) PrepareAndroidMKProviderInfo(config android.Config) *android.AndroidMkProviderInfo { 319 providerData := android.AndroidMkProviderInfo{ 320 PrimaryInfo: android.AndroidMkInfo{ 321 Class: "ETC", 322 OutputFile: android.OptionalPathForPath(v.output), 323 EntryMap: make(map[string][]string), 324 }, 325 } 326 providerData.PrimaryInfo.SetString("LOCAL_MODULE_PATH", v.installDir.String()) 327 providerData.PrimaryInfo.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) 328 return &providerData 329} 330 331var _ Filesystem = (*vbmeta)(nil) 332 333func (v *vbmeta) OutputPath() android.Path { 334 return v.output 335} 336 337func (v *vbmeta) SignedOutputPath() android.Path { 338 return v.OutputPath() // vbmeta is always signed 339} 340