1// Copyright (C) 2024 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 fsgen 16 17import ( 18 "android/soong/android" 19 "android/soong/filesystem" 20 "strconv" 21 22 "github.com/google/blueprint/proptools" 23) 24 25type vbmetaModuleInfo struct { 26 // The name of the generated vbmeta module 27 moduleName string 28 // The name of the module that avb understands. This is the name passed to --chain_partition, 29 // and also the basename of the output file. (the output file is called partitionName + ".img") 30 partitionName string 31} 32 33// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4849;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901 34var avbPartitions = []string{ 35 "boot", 36 "init_boot", 37 "vendor_boot", 38 "vendor_kernel_boot", 39 "system", 40 "vendor", 41 "product", 42 "system_ext", 43 "odm", 44 "vendor_dlkm", 45 "odm_dlkm", 46 "system_dlkm", 47 "dtbo", 48 "pvmfw", 49 "recovery", 50 "vbmeta_system", 51 "vbmeta_vendor", 52} 53 54// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names 55// that the function created. May return nil if the product isn't using avb. 56// 57// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot 58// It works by signing all the partitions, but then also including an extra metadata paritition 59// called vbmeta that depends on all the other signed partitions. This creates a requirement 60// that you update all those partitions and the vbmeta partition together, so in order to relax 61// that requirement products can set up "chained" vbmeta partitions, where a chained partition 62// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish 63// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions, 64// that group of partitions can be updated independently from the other signed partitions. 65func (f *filesystemCreator) createVbmetaPartitions(ctx android.LoadHookContext, partitions allGeneratedPartitionData) []vbmetaModuleInfo { 66 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse 67 // Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false 68 if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable { 69 return nil 70 } 71 72 var result []vbmetaModuleInfo 73 74 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4593;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901 75 var internalAvbPartitionsInChainedVbmetaImages []string 76 var chainedPartitionTypes []string 77 for _, chainedName := range android.SortedKeys(partitionVars.ChainedVbmetaPartitions) { 78 props := partitionVars.ChainedVbmetaPartitions[chainedName] 79 filesystemPartitionType := chainedName 80 chainedName = "vbmeta_" + chainedName 81 if len(props.Partitions) == 0 { 82 continue 83 } 84 internalAvbPartitionsInChainedVbmetaImages = append(internalAvbPartitionsInChainedVbmetaImages, props.Partitions...) 85 if len(props.Key) == 0 { 86 ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName) 87 continue 88 } 89 if len(props.Algorithm) == 0 { 90 ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName) 91 continue 92 } 93 if len(props.RollbackIndex) == 0 { 94 ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName) 95 continue 96 } 97 ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32) 98 if err != nil { 99 ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation) 100 continue 101 } 102 // The default is to use the PlatformSecurityPatch, and a lot of product config files 103 // just set it to the platform security patch, so detect that and don't set the property 104 // in soong. 105 var rollbackIndex *int64 106 if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() { 107 i, err := strconv.ParseInt(props.RollbackIndex, 10, 32) 108 if err != nil { 109 ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex) 110 continue 111 } 112 rollbackIndex = &i 113 } 114 115 var partitionModules []string 116 for _, partition := range props.Partitions { 117 if modName := partitions.nameForType(partition); modName != "" { 118 partitionModules = append(partitionModules, modName) 119 } 120 } 121 122 name := generatedModuleNameForPartition(ctx.Config(), chainedName) 123 ctx.CreateModuleInDirectory( 124 filesystem.VbmetaFactory, 125 ".", // Create in the root directory for now so its easy to get the key 126 &filesystem.VbmetaProperties{ 127 Partition_name: proptools.StringPtr(chainedName), 128 Filesystem_partition_type: proptools.StringPtr(filesystemPartitionType), 129 Stem: proptools.StringPtr(chainedName + ".img"), 130 Private_key: proptools.StringPtr(props.Key), 131 Algorithm: &props.Algorithm, 132 Rollback_index: rollbackIndex, 133 Rollback_index_location: &ril, 134 Partitions: proptools.NewSimpleConfigurable(partitionModules), 135 }, &struct { 136 Name *string 137 }{ 138 Name: &name, 139 }, 140 ).HideFromMake() 141 142 result = append(result, vbmetaModuleInfo{ 143 moduleName: name, 144 partitionName: chainedName, 145 }) 146 147 chainedPartitionTypes = append(chainedPartitionTypes, chainedName) 148 } 149 150 vbmetaModuleName := generatedModuleNameForPartition(ctx.Config(), "vbmeta") 151 152 var algorithm *string 153 var ri *int64 154 var key *string 155 if len(partitionVars.BoardAvbKeyPath) == 0 { 156 // Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0 157 key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem") 158 algorithm = proptools.StringPtr("SHA256_RSA4096") 159 } else { 160 key = proptools.StringPtr(partitionVars.BoardAvbKeyPath) 161 algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm) 162 } 163 if len(partitionVars.BoardAvbRollbackIndex) > 0 { 164 parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32) 165 if err != nil { 166 ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex) 167 } 168 ri = &parsedRi 169 } 170 171 // --chain_partition argument is only set for partitions that set 172 // `BOARD_AVB_<partition name>_KEY_PATH` value and is not "recovery" 173 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4823;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901 174 includeAsChainedPartitionInVbmeta := func(partition string) bool { 175 val, ok := partitionVars.PartitionQualifiedVariables[partition] 176 return ok && len(val.BoardAvbKeyPath) > 0 && partition != "recovery" 177 } 178 179 // --include_descriptors_from_image is passed if both conditions are met: 180 // - `BOARD_AVB_<partition name>_KEY_PATH` value is not set 181 // - not included in INTERNAL_AVB_PARTITIONS_IN_CHAINED_VBMETA_IMAGES 182 // for partitions that set INSTALLED_<partition name>IMAGE_TARGET 183 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4827;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901 184 includeAsIncludedPartitionInVbmeta := func(partition string) bool { 185 if android.InList(partition, internalAvbPartitionsInChainedVbmetaImages) { 186 // Already handled by chained vbmeta partitions 187 return false 188 } 189 partitionQualifiedVars := partitionVars.PartitionQualifiedVariables[partition] 190 191 // The return logic in the switch cases below are identical to 192 // ifdef INSTALLED_<partition name>IMAGE_TARGET 193 switch partition { 194 case "boot": 195 return partitionQualifiedVars.BuildingImage || partitionQualifiedVars.PrebuiltImage || partitionVars.BoardUsesRecoveryAsBoot 196 case "vendor_kernel_boot", "dtbo": 197 return partitionQualifiedVars.PrebuiltImage 198 case "system": 199 return partitionQualifiedVars.BuildingImage 200 case "init_boot", "vendor_boot", "vendor", "product", "system_ext", "odm", "vendor_dlkm", "odm_dlkm", "system_dlkm": 201 return partitionQualifiedVars.BuildingImage || partitionQualifiedVars.PrebuiltImage 202 // TODO: Import BOARD_USES_PVMFWIMAGE 203 // ifeq ($(BOARD_USES_PVMFWIMAGE),true) 204 // case "pvmfw": 205 case "recovery": 206 // ifdef INSTALLED_RECOVERYIMAGE_TARGET 207 return !ctx.DeviceConfig().BoardUsesRecoveryAsBoot() && !ctx.DeviceConfig().BoardMoveRecoveryResourcesToVendorBoot() 208 // Technically these partitions are determined based on len(BOARD_AVB_VBMETA_SYSTEM) and 209 // len(BOARD_AVB_VBMETA_VENDOR) but if these are non empty these partitions are 210 // already included in the chained partitions. 211 case "vbmeta_system", "vbmeta_vendor": 212 return false 213 default: 214 return false 215 } 216 } 217 218 var chainedPartitionModules []string 219 var includePartitionModules []string 220 allGeneratedPartitionTypes := append(partitions.types(), 221 chainedPartitionTypes..., 222 ) 223 if len(f.properties.Boot_image) > 0 { 224 allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "boot") 225 } 226 if len(f.properties.Init_boot_image) > 0 { 227 allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "init_boot") 228 } 229 if len(f.properties.Vendor_boot_image) > 0 { 230 allGeneratedPartitionTypes = append(allGeneratedPartitionTypes, "vendor_boot") 231 } 232 233 // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4919;drc=62e20f0d218f60bae563b4ee742d88cca1fc1901 234 for _, partitionType := range android.SortedUniqueStrings(append(avbPartitions, chainedPartitionTypes...)) { 235 if !android.InList(partitionType, allGeneratedPartitionTypes) { 236 // Skip if the partition is not auto generated 237 continue 238 } 239 name := partitions.nameForType(partitionType) 240 if name == "" { 241 name = generatedModuleNameForPartition(ctx.Config(), partitionType) 242 } 243 if includeAsChainedPartitionInVbmeta(partitionType) { 244 chainedPartitionModules = append(chainedPartitionModules, name) 245 } else if includeAsIncludedPartitionInVbmeta(partitionType) { 246 includePartitionModules = append(includePartitionModules, name) 247 } 248 } 249 250 ctx.CreateModuleInDirectory( 251 filesystem.VbmetaFactory, 252 ".", // Create in the root directory for now so its easy to get the key 253 &filesystem.VbmetaProperties{ 254 Stem: proptools.StringPtr("vbmeta.img"), 255 Algorithm: algorithm, 256 Private_key: key, 257 Rollback_index: ri, 258 Chained_partitions: chainedPartitionModules, 259 Partitions: proptools.NewSimpleConfigurable(includePartitionModules), 260 Partition_name: proptools.StringPtr("vbmeta"), 261 }, &struct { 262 Name *string 263 }{ 264 Name: &vbmetaModuleName, 265 }, 266 ).HideFromMake() 267 268 result = append(result, vbmetaModuleInfo{ 269 moduleName: vbmetaModuleName, 270 partitionName: "vbmeta", 271 }) 272 return result 273} 274