1// Copyright (C) 2020 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 15// A special genrule that creates OTA payload and payload_properties from a raw 16// image. This rule is created so that the two outputs, payload and 17// payload_properties, can be distinguished with tags. 18 19package gki 20 21import ( 22 "fmt" 23 "sort" 24 "strings" 25 26 "android/soong/android" 27 "android/soong/java" 28 29 "github.com/google/blueprint" 30 "github.com/google/blueprint/proptools" 31) 32 33type dependencyTag struct { 34 blueprint.BaseDependencyTag 35 name string 36} 37 38// {"foo": "fooVal", "bar": "barVal"} -> ["${foo}", "${bar}"] 39func keysToVars(deps map[string]string) []string { 40 var ret []string 41 for dep := range deps { 42 ret = append(ret, fmt.Sprintf("${%s}", dep)) 43 } 44 sort.Strings(ret) 45 return ret 46} 47 48var ( 49 certificateTag = dependencyTag{name: "certificate"} 50 rawImageTag = dependencyTag{name: "raw_image"} 51 52 pctx = android.NewPackageContext("android/gki") 53 54 otaFromRawImageDeps = map[string]string{ 55 "ota_from_raw_image": "ota_from_raw_image", 56 57 // Needed by ota_from_target_files 58 "brillo_update_payload": "brillo_update_payload", 59 60 // Needed by brillo_update_payload 61 "delta_generator": "delta_generator", 62 // b/171581299: shflags isn't built to the path where HostBinToolVariable 63 // points to without explicitly declaring it, even if it is stated as 64 // required by brillo_update_payload. 65 "shflags": "lib/shflags/shflags", 66 67 // Needed by GetBootImageTimestamp 68 "lz4": "lz4", 69 "toybox": "toybox", 70 "unpack_bootimg": "unpack_bootimg", 71 } 72 73 otaFromRawImageVarDeps = keysToVars(otaFromRawImageDeps) 74 75 otaFromRawImageRule = pctx.AndroidStaticRule("ota_from_raw_image", blueprint.RuleParams{ 76 Command: `${ota_from_raw_image} --tools ` + strings.Join(otaFromRawImageVarDeps, " ") + 77 ` ${kwargs} --out ${outDir} -- ${inputArg}`, 78 CommandDeps: otaFromRawImageVarDeps, 79 Description: "ota_from_raw_image ${outDir}", 80 }, "kwargs", "outDir", "inputArg") 81 82 // Tags to OutputFiles 83 payloadTag = "payload" 84 payloadPropertiesTag = "properties" 85) 86 87func init() { 88 for dep := range otaFromRawImageDeps { 89 pctx.HostBinToolVariable(dep, otaFromRawImageDeps[dep]) 90 } 91 // Intentionally not register this module so that it can only be constructed by gki_apex. 92} 93 94type rawImageOtaProperties struct { 95 // The name of a certificate in the default certificate directory, blank to use the default product certificate, 96 // or an android_app_certificate module name in the form ":module". 97 Certificate *string 98 99 // A set of images and their related modules. Must be in this form 100 // IMAGE_NAME:MODULE, where IMAGE_NAME is an image name like "boot", and 101 // MODULE is the name of a makefile_goal. 102 Image_goals []string 103} 104 105type rawImageOta struct { 106 android.ModuleBase 107 properties rawImageOtaProperties 108 109 pem android.Path 110 key android.Path 111 112 outPayload android.WritablePath 113 outProperties android.WritablePath 114} 115 116// Declare a rule that generates a signed OTA payload from a raw image. This 117// includes payload.bin and payload_properties.txt. 118func rawImageOtaFactory() android.Module { 119 r := &rawImageOta{} 120 r.AddProperties(&r.properties) 121 android.InitAndroidModule(r) 122 return r 123} 124 125func (r *rawImageOta) OutputFiles(tag string) (android.Paths, error) { 126 switch tag { 127 case "": 128 return android.Paths{r.outPayload, r.outProperties}, nil 129 case payloadTag: 130 return android.Paths{r.outPayload}, nil 131 case payloadPropertiesTag: 132 return android.Paths{r.outProperties}, nil 133 default: 134 return nil, fmt.Errorf("unsupported module reference tag %q", tag) 135 } 136} 137 138var _ android.OutputFileProducer = (*rawImageOta)(nil) 139 140func (r *rawImageOta) getCertString(ctx android.BaseModuleContext) string { 141 moduleName := ctx.ModuleName() 142 certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName) 143 if overridden { 144 return ":" + certificate 145 } 146 return proptools.String(r.properties.Certificate) 147} 148 149// Returns module->image_name mapping, e.g. "bootimage_soong"->"boot" 150func (r *rawImageOta) goalToImage(ctx android.EarlyModuleContext) map[string]string { 151 ret := map[string]string{} 152 for _, imageGoal := range r.properties.Image_goals { 153 lst := strings.Split(imageGoal, ":") 154 if len(lst) != 2 { 155 ctx.PropertyErrorf("image_goals", "Must be in the form IMAGE_NAME:MODULE") 156 return map[string]string{} 157 } 158 ret[lst[1]] = lst[0] 159 } 160 return ret 161} 162 163func (r *rawImageOta) DepsMutator(ctx android.BottomUpMutatorContext) { 164 // Add dependency to modules in image_goals 165 for module, _ := range r.goalToImage(ctx) { 166 ctx.AddVariationDependencies(nil, rawImageTag, module) 167 } 168 // Add dependency to certificate module, if any. 169 cert := android.SrcIsModule(r.getCertString(ctx)) 170 if cert != "" { 171 ctx.AddVariationDependencies(nil, certificateTag, cert) 172 } 173} 174 175func (r *rawImageOta) GenerateAndroidBuildActions(ctx android.ModuleContext) { 176 inputArg := []string{} 177 kwargs := []string{} 178 implicits := android.Paths{} 179 180 // Handle image_goals 181 goalToImage := r.goalToImage(ctx) 182 ctx.VisitDirectDepsWithTag(rawImageTag, func(module android.Module) { 183 depName := ctx.OtherModuleName(module) 184 imgPath := android.OutputFileForModule(ctx, module, "") 185 if imgPath != nil { 186 implicits = append(implicits, imgPath) 187 inputArg = append(inputArg, goalToImage[depName]+":"+imgPath.String()) 188 } else { 189 ctx.ModuleErrorf("image dependency %q does not generate any output", depName) 190 } 191 }) 192 193 // Handle certificate 194 ctx.VisitDirectDepsWithTag(certificateTag, func(module android.Module) { 195 depName := ctx.OtherModuleName(module) 196 if cert, ok := module.(*java.AndroidAppCertificate); ok { 197 r.pem = cert.Certificate.Pem 198 r.key = cert.Certificate.Key 199 } else { 200 ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName) 201 } 202 }) 203 r.setCertificateAndPrivateKey(ctx) 204 keyName, keyError := removeCertExt(r.pem) 205 if keyError != nil { 206 ctx.ModuleErrorf("Cannot get certificate to sign the OTA payload binary: " + keyError.Error()) 207 } 208 implicits = append(implicits, r.pem, r.key) 209 kwargs = append(kwargs, "--key "+proptools.String(keyName)) 210 211 // Set outputs 212 outDir := android.PathForModuleGen(ctx, "payload_files") 213 r.outPayload = outDir.Join(ctx, "payload.bin") 214 r.outProperties = outDir.Join(ctx, "payload_properties.txt") 215 216 ctx.Build(pctx, android.BuildParams{ 217 Rule: otaFromRawImageRule, 218 Description: "Generate OTA from raw image", 219 Implicits: implicits, 220 Outputs: android.WritablePaths{r.outPayload, r.outProperties}, 221 Args: map[string]string{ 222 "kwargs": strings.Join(kwargs, " "), 223 "outDir": outDir.String(), 224 "inputArg": strings.Join(inputArg, " "), 225 }, 226 }) 227} 228 229func (r *rawImageOta) setCertificateAndPrivateKey(ctx android.ModuleContext) { 230 if r.pem == nil { 231 cert := proptools.String(r.properties.Certificate) 232 if cert == "" { 233 pem, key := ctx.Config().DefaultAppCertificate(ctx) 234 r.pem = pem 235 r.key = key 236 } else { 237 defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) 238 r.pem = defaultDir.Join(ctx, cert+".x509.pem") 239 r.key = defaultDir.Join(ctx, cert+".pk8") 240 } 241 } 242} 243 244func removeCertExt(path android.Path) (*string, error) { 245 s := path.String() 246 if strings.HasSuffix(s, ".x509.pem") { 247 return proptools.StringPtr(strings.TrimSuffix(s, ".x509.pem")), nil 248 } 249 return nil, fmt.Errorf("Path %q does not end with .x509.pem", s) 250} 251