// Copyright (C) 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package filesystem import ( "fmt" "strconv" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" ) func init() { android.RegisterModuleType("bootimg", bootimgFactory) } type bootimg struct { android.ModuleBase properties bootimgProperties output android.OutputPath installDir android.InstallPath } type bootimgProperties struct { // Set the name of the output. Defaults to .img. Stem *string // Path to the linux kernel prebuilt file Kernel_prebuilt *string `android:"arch_variant,path"` // Filesystem module that is used as ramdisk Ramdisk_module *string // Path to the device tree blob (DTB) prebuilt file to add to this boot image Dtb_prebuilt *string `android:"arch_variant,path"` // Header version number. Must be set to one of the version numbers that are currently // supported. Refer to // https://source.android.com/devices/bootloader/boot-image-header Header_version *string // Determines if this image is for the vendor_boot partition. Default is false. Refer to // https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions Vendor_boot *bool // Optional kernel commandline Cmdline *string // When set to true, sign the image with avbtool. Default is false. Use_avb *bool // Name of the partition stored in vbmeta desc. Defaults to the name of this module. Partition_name *string // Path to the private key that avbtool will use to sign this filesystem image. // TODO(jiyong): allow apex_key to be specified here Avb_private_key *string `android:"path"` // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096. Avb_algorithm *string } // bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb. func bootimgFactory() android.Module { module := &bootimg{} module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) return module } type bootimgDep struct { blueprint.BaseDependencyTag kind string } var bootimgRamdiskDep = bootimgDep{kind: "ramdisk"} func (b *bootimg) DepsMutator(ctx android.BottomUpMutatorContext) { ramdisk := proptools.String(b.properties.Ramdisk_module) if ramdisk != "" { ctx.AddDependency(ctx.Module(), bootimgRamdiskDep, ramdisk) } } func (b *bootimg) installFileName() string { return proptools.StringDefault(b.properties.Stem, b.BaseModuleName()+".img") } func (b *bootimg) partitionName() string { return proptools.StringDefault(b.properties.Partition_name, b.BaseModuleName()) } func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { var unsignedOutput android.OutputPath if proptools.Bool(b.properties.Vendor_boot) { unsignedOutput = b.buildVendorBootImage(ctx) } else { // TODO(jiyong): fix this ctx.PropertyErrorf("vendor_boot", "only vendor_boot:true is supported") return } if proptools.Bool(b.properties.Use_avb) { b.output = b.signImage(ctx, unsignedOutput) } else { b.output = unsignedOutput } b.installDir = android.PathForModuleInstall(ctx, "etc") ctx.InstallFile(b.installDir, b.installFileName(), b.output) } func (b *bootimg) buildVendorBootImage(ctx android.ModuleContext) android.OutputPath { output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()).OutputPath builder := android.NewRuleBuilder(pctx, ctx) cmd := builder.Command().BuiltTool("mkbootimg") kernel := android.OptionalPathForModuleSrc(ctx, b.properties.Kernel_prebuilt) if kernel.Valid() { ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") return output } dtbName := proptools.String(b.properties.Dtb_prebuilt) if dtbName == "" { ctx.PropertyErrorf("dtb_prebuilt", "must be set") return output } dtb := android.PathForModuleSrc(ctx, dtbName) cmd.FlagWithInput("--dtb ", dtb) cmdline := proptools.String(b.properties.Cmdline) if cmdline != "" { cmd.FlagWithArg("--vendor_cmdline ", "\""+cmdline+"\"") } headerVersion := proptools.String(b.properties.Header_version) if headerVersion == "" { ctx.PropertyErrorf("header_version", "must be set") return output } verNum, err := strconv.Atoi(headerVersion) if err != nil { ctx.PropertyErrorf("header_version", "%q is not a number", headerVersion) return output } if verNum < 3 { ctx.PropertyErrorf("header_version", "must be 3 or higher for vendor_boot") return output } cmd.FlagWithArg("--header_version ", headerVersion) ramdiskName := proptools.String(b.properties.Ramdisk_module) if ramdiskName == "" { ctx.PropertyErrorf("ramdisk_module", "must be set") return output } ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep) if filesystem, ok := ramdisk.(*filesystem); ok { cmd.FlagWithInput("--vendor_ramdisk ", filesystem.OutputPath()) } else { ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name()) return output } cmd.FlagWithOutput("--vendor_boot ", output) builder.Build("build_vendor_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName())) return output } func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.OutputPath) android.OutputPath { output := android.PathForModuleOut(ctx, b.installFileName()).OutputPath key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)) builder := android.NewRuleBuilder(pctx, ctx) builder.Command().Text("cp").Input(unsignedImage).Output(output) builder.Command(). BuiltTool("avbtool"). Flag("add_hash_footer"). FlagWithArg("--partition_name ", b.partitionName()). FlagWithInput("--key ", key). FlagWithOutput("--image ", output) builder.Build("sign_bootimg", fmt.Sprintf("Signing %s", b.BaseModuleName())) return output } var _ android.AndroidMkEntriesProvider = (*bootimg)(nil) // Implements android.AndroidMkEntriesProvider func (b *bootimg) AndroidMkEntries() []android.AndroidMkEntries { return []android.AndroidMkEntries{android.AndroidMkEntries{ Class: "ETC", OutputFile: android.OptionalPathForPath(b.output), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(entries *android.AndroidMkEntries) { entries.SetString("LOCAL_MODULE_PATH", b.installDir.ToMakePath().String()) entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName()) }, }, }} } var _ Filesystem = (*bootimg)(nil) func (b *bootimg) OutputPath() android.Path { return b.output }