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} 30 31type vbmeta struct { 32 android.ModuleBase 33 34 properties vbmetaProperties 35 36 output android.OutputPath 37 installDir android.InstallPath 38} 39 40type vbmetaProperties struct { 41 // Name of the partition stored in vbmeta desc. Defaults to the name of this module. 42 Partition_name *string 43 44 // Set the name of the output. Defaults to <module_name>.img. 45 Stem *string 46 47 // Path to the private key that avbtool will use to sign this vbmeta image. 48 Private_key *string `android:"path"` 49 50 // Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096. 51 Algorithm *string 52 53 // File whose content will provide the rollback index. If unspecified, the rollback index 54 // is from PLATFORM_SECURITY_PATCH 55 Rollback_index_file *string `android:"path"` 56 57 // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0. 58 Rollback_index_location *int64 59 60 // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules 61 // have to be signed (use_avb: true). 62 Partitions []string 63 64 // List of chained partitions that this vbmeta deletages the verification. 65 Chained_partitions []chainedPartitionProperties 66} 67 68type chainedPartitionProperties struct { 69 // Name of the chained partition 70 Name *string 71 72 // Rollback index location of the chained partition. Must be 0, 1, 2, etc. Default is the 73 // index of this partition in the list + 1. 74 Rollback_index_location *int64 75 76 // Path to the public key that the chained partition is signed with. If this is specified, 77 // private_key is ignored. 78 Public_key *string `android:"path"` 79 80 // Path to the private key that the chained partition is signed with. If this is specified, 81 // and public_key is not specified, a public key is extracted from this private key and 82 // the extracted public key is embedded in the vbmeta image. 83 Private_key *string `android:"path"` 84} 85 86// vbmeta is the partition image that has the verification information for other partitions. 87func vbmetaFactory() android.Module { 88 module := &vbmeta{} 89 module.AddProperties(&module.properties) 90 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) 91 return module 92} 93 94type vbmetaDep struct { 95 blueprint.BaseDependencyTag 96 kind string 97} 98 99var vbmetaPartitionDep = vbmetaDep{kind: "partition"} 100 101func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) { 102 ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...) 103} 104 105func (v *vbmeta) installFileName() string { 106 return proptools.StringDefault(v.properties.Stem, v.BaseModuleName()+".img") 107} 108 109func (v *vbmeta) partitionName() string { 110 return proptools.StringDefault(v.properties.Partition_name, v.BaseModuleName()) 111} 112 113// See external/avb/libavb/avb_slot_verify.c#VBMETA_MAX_SIZE 114const vbmetaMaxSize = 64 * 1024 115 116func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { 117 extractedPublicKeys := v.extractPublicKeys(ctx) 118 119 v.output = android.PathForModuleOut(ctx, v.installFileName()).OutputPath 120 121 builder := android.NewRuleBuilder(pctx, ctx) 122 cmd := builder.Command().BuiltTool("avbtool").Text("make_vbmeta_image") 123 124 key := android.PathForModuleSrc(ctx, proptools.String(v.properties.Private_key)) 125 cmd.FlagWithInput("--key ", key) 126 127 algorithm := proptools.StringDefault(v.properties.Algorithm, "SHA256_RSA4096") 128 cmd.FlagWithArg("--algorithm ", algorithm) 129 130 cmd.FlagWithArg("--rollback_index ", v.rollbackIndexCommand(ctx)) 131 ril := proptools.IntDefault(v.properties.Rollback_index_location, 0) 132 if ril < 0 { 133 ctx.PropertyErrorf("rollback_index_location", "must be 0, 1, 2, ...") 134 return 135 } 136 cmd.FlagWithArg("--rollback_index_location ", strconv.Itoa(ril)) 137 138 for _, p := range ctx.GetDirectDepsWithTag(vbmetaPartitionDep) { 139 f, ok := p.(Filesystem) 140 if !ok { 141 ctx.PropertyErrorf("partitions", "%q(type: %s) is not supported", 142 p.Name(), ctx.OtherModuleType(p)) 143 continue 144 } 145 signedImage := f.SignedOutputPath() 146 if signedImage == nil { 147 ctx.PropertyErrorf("partitions", "%q(type: %s) is not signed. Use `use_avb: true`", 148 p.Name(), ctx.OtherModuleType(p)) 149 continue 150 } 151 cmd.FlagWithInput("--include_descriptors_from_image ", signedImage) 152 } 153 154 for i, cp := range v.properties.Chained_partitions { 155 name := proptools.String(cp.Name) 156 if name == "" { 157 ctx.PropertyErrorf("chained_partitions", "name must be specified") 158 continue 159 } 160 161 ril := proptools.IntDefault(cp.Rollback_index_location, i+1) 162 if ril < 0 { 163 ctx.PropertyErrorf("chained_partitions", "must be 0, 1, 2, ...") 164 continue 165 } 166 167 var publicKey android.Path 168 if cp.Public_key != nil { 169 publicKey = android.PathForModuleSrc(ctx, proptools.String(cp.Public_key)) 170 } else { 171 publicKey = extractedPublicKeys[name] 172 } 173 cmd.FlagWithArg("--chain_partition ", fmt.Sprintf("%s:%d:%s", name, ril, publicKey.String())) 174 cmd.Implicit(publicKey) 175 } 176 177 cmd.FlagWithOutput("--output ", v.output) 178 179 // libavb expects to be able to read the maximum vbmeta size, so we must provide a partition 180 // which matches this or the read will fail. 181 builder.Command().Text("truncate"). 182 FlagWithArg("-s ", strconv.Itoa(vbmetaMaxSize)). 183 Output(v.output) 184 185 builder.Build("vbmeta", fmt.Sprintf("vbmeta %s", ctx.ModuleName())) 186 187 v.installDir = android.PathForModuleInstall(ctx, "etc") 188 ctx.InstallFile(v.installDir, v.installFileName(), v.output) 189} 190 191// Returns the embedded shell command that prints the rollback index 192func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string { 193 var cmd string 194 if v.properties.Rollback_index_file != nil { 195 f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file)) 196 cmd = "cat " + f.String() 197 } else { 198 cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s" 199 } 200 // Take the first line and remove the newline char 201 return "$(" + cmd + " | head -1 | tr -d '\n'" + ")" 202} 203 204// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition 205// name. 206func (v *vbmeta) extractPublicKeys(ctx android.ModuleContext) map[string]android.OutputPath { 207 result := make(map[string]android.OutputPath) 208 209 builder := android.NewRuleBuilder(pctx, ctx) 210 for _, cp := range v.properties.Chained_partitions { 211 if cp.Private_key == nil { 212 continue 213 } 214 215 name := proptools.String(cp.Name) 216 if name == "" { 217 ctx.PropertyErrorf("chained_partitions", "name must be specified") 218 continue 219 } 220 221 if _, ok := result[name]; ok { 222 ctx.PropertyErrorf("chained_partitions", "name %q is duplicated", name) 223 continue 224 } 225 226 privateKeyFile := android.PathForModuleSrc(ctx, proptools.String(cp.Private_key)) 227 publicKeyFile := android.PathForModuleOut(ctx, name+".avbpubkey").OutputPath 228 229 builder.Command(). 230 BuiltTool("avbtool"). 231 Text("extract_public_key"). 232 FlagWithInput("--key ", privateKeyFile). 233 FlagWithOutput("--output ", publicKeyFile) 234 235 result[name] = publicKeyFile 236 } 237 builder.Build("vbmeta_extract_public_key", fmt.Sprintf("Extract public keys for %s", ctx.ModuleName())) 238 return result 239} 240 241var _ android.AndroidMkEntriesProvider = (*vbmeta)(nil) 242 243// Implements android.AndroidMkEntriesProvider 244func (v *vbmeta) AndroidMkEntries() []android.AndroidMkEntries { 245 return []android.AndroidMkEntries{android.AndroidMkEntries{ 246 Class: "ETC", 247 OutputFile: android.OptionalPathForPath(v.output), 248 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 249 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 250 entries.SetString("LOCAL_MODULE_PATH", v.installDir.ToMakePath().String()) 251 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName()) 252 }, 253 }, 254 }} 255} 256 257var _ Filesystem = (*vbmeta)(nil) 258 259func (v *vbmeta) OutputPath() android.Path { 260 return v.output 261} 262 263func (v *vbmeta) SignedOutputPath() android.Path { 264 return v.OutputPath() // vbmeta is always signed 265} 266 267var _ android.OutputFileProducer = (*vbmeta)(nil) 268 269// Implements android.OutputFileProducer 270func (v *vbmeta) OutputFiles(tag string) (android.Paths, error) { 271 if tag == "" { 272 return []android.Path{v.output}, nil 273 } 274 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 275} 276