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 "slices" 21 "strconv" 22 "strings" 23 24 "github.com/google/blueprint/proptools" 25) 26 27type vbmetaModuleInfo struct { 28 // The name of the generated vbmeta module 29 moduleName string 30 // The name of the module that avb understands. This is the name passed to --chain_partition, 31 // and also the basename of the output file. (the output file is called partitionName + ".img") 32 partitionName string 33} 34 35// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names 36// that the function created. May return nil if the product isn't using avb. 37// 38// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot 39// It works by signing all the partitions, but then also including an extra metadata paritition 40// called vbmeta that depends on all the other signed partitions. This creates a requirement 41// that you update all those partitions and the vbmeta partition together, so in order to relax 42// that requirement products can set up "chained" vbmeta partitions, where a chained partition 43// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish 44// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions, 45// that group of partitions can be updated independently from the other signed partitions. 46func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo { 47 partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse 48 // Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false 49 if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable { 50 return nil 51 } 52 53 var result []vbmetaModuleInfo 54 55 var chainedPartitions []string 56 var partitionTypesHandledByChainedPartitions []string 57 for _, chainedName := range android.SortedKeys(partitionVars.ChainedVbmetaPartitions) { 58 props := partitionVars.ChainedVbmetaPartitions[chainedName] 59 chainedName = "vbmeta_" + chainedName 60 if len(props.Partitions) == 0 { 61 continue 62 } 63 if len(props.Key) == 0 { 64 ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName) 65 continue 66 } 67 if len(props.Algorithm) == 0 { 68 ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName) 69 continue 70 } 71 if len(props.RollbackIndex) == 0 { 72 ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName) 73 continue 74 } 75 ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32) 76 if err != nil { 77 ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation) 78 continue 79 } 80 // The default is to use the PlatformSecurityPatch, and a lot of product config files 81 // just set it to the platform security patch, so detect that and don't set the property 82 // in soong. 83 var rollbackIndex *int64 84 if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() { 85 i, err := strconv.ParseInt(props.RollbackIndex, 10, 32) 86 if err != nil { 87 ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex) 88 continue 89 } 90 rollbackIndex = &i 91 } 92 93 var partitionModules []string 94 for _, partition := range props.Partitions { 95 partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition) 96 if !slices.Contains(generatedPartitionTypes, partition) { 97 // The partition is probably unsupported. 98 continue 99 } 100 partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition)) 101 } 102 103 name := generatedModuleName(ctx.Config(), chainedName) 104 ctx.CreateModuleInDirectory( 105 filesystem.VbmetaFactory, 106 ".", // Create in the root directory for now so its easy to get the key 107 &filesystem.VbmetaProperties{ 108 Partition_name: proptools.StringPtr(chainedName), 109 Stem: proptools.StringPtr(chainedName + ".img"), 110 Private_key: proptools.StringPtr(props.Key), 111 Algorithm: &props.Algorithm, 112 Rollback_index: rollbackIndex, 113 Rollback_index_location: &ril, 114 Partitions: proptools.NewSimpleConfigurable(partitionModules), 115 }, &struct { 116 Name *string 117 }{ 118 Name: &name, 119 }, 120 ).HideFromMake() 121 122 chainedPartitions = append(chainedPartitions, name) 123 124 result = append(result, vbmetaModuleInfo{ 125 moduleName: name, 126 partitionName: chainedName, 127 }) 128 } 129 130 vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta") 131 132 var algorithm *string 133 var ri *int64 134 var key *string 135 if len(partitionVars.BoardAvbKeyPath) == 0 { 136 // Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0 137 key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem") 138 algorithm = proptools.StringPtr("SHA256_RSA4096") 139 } else { 140 key = proptools.StringPtr(partitionVars.BoardAvbKeyPath) 141 algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm) 142 } 143 if len(partitionVars.BoardAvbRollbackIndex) > 0 { 144 parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32) 145 if err != nil { 146 ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex) 147 } 148 ri = &parsedRi 149 } 150 151 var partitionModules []string 152 for _, partitionType := range generatedPartitionTypes { 153 if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) { 154 // Already handled by a chained vbmeta partition 155 continue 156 } 157 if strings.Contains(partitionType, "ramdisk") || strings.Contains(partitionType, "boot") { 158 // ramdisk is never signed with avb information 159 // boot partitions just have the avb footer, and don't have a corresponding vbmeta 160 // partition. 161 continue 162 } 163 partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType)) 164 } 165 166 ctx.CreateModuleInDirectory( 167 filesystem.VbmetaFactory, 168 ".", // Create in the root directory for now so its easy to get the key 169 &filesystem.VbmetaProperties{ 170 Stem: proptools.StringPtr("vbmeta.img"), 171 Algorithm: algorithm, 172 Private_key: key, 173 Rollback_index: ri, 174 Chained_partitions: chainedPartitions, 175 Partitions: proptools.NewSimpleConfigurable(partitionModules), 176 }, &struct { 177 Name *string 178 }{ 179 Name: &vbmetaModuleName, 180 }, 181 ).HideFromMake() 182 183 result = append(result, vbmetaModuleInfo{ 184 moduleName: vbmetaModuleName, 185 partitionName: "vbmeta", 186 }) 187 return result 188} 189