diff options
Diffstat (limited to 'filesystem')
-rw-r--r-- | filesystem/bootimg.go | 181 | ||||
-rw-r--r-- | filesystem/filesystem.go | 45 | ||||
-rw-r--r-- | filesystem/filesystem_test.go | 33 |
3 files changed, 232 insertions, 27 deletions
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go index c9bd61788..90409b318 100644 --- a/filesystem/bootimg.go +++ b/filesystem/bootimg.go @@ -26,19 +26,21 @@ import ( ) func init() { - android.RegisterModuleType("bootimg", bootimgFactory) + android.RegisterModuleType("bootimg", BootimgFactory) } type bootimg struct { android.ModuleBase - properties bootimgProperties + properties BootimgProperties output android.Path installDir android.InstallPath + + bootImageType bootImageType } -type bootimgProperties struct { +type BootimgProperties struct { // Set the name of the output. Defaults to <module_name>.img. Stem *string @@ -56,9 +58,13 @@ type bootimgProperties struct { // 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 + // Determines the specific type of boot image this module is building. Can be boot, + // vendor_boot or init_boot. Defaults to boot. + // Refer to https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions + // for vendor_boot. + // Refer to https://source.android.com/docs/core/architecture/partitions/generic-boot for + // init_boot. + Boot_image_type *string // Optional kernel commandline arguments Cmdline []string `android:"arch_variant"` @@ -67,6 +73,10 @@ type bootimgProperties struct { // and `header_version` is greater than or equal to 4. Bootconfig *string `android:"arch_variant,path"` + // The size of the partition on the device. It will be a build error if this built partition + // image exceeds this size. + Partition_size *int64 + // When set to true, sign the image with avbtool. Default is false. Use_avb *bool @@ -79,10 +89,62 @@ type bootimgProperties struct { // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096. Avb_algorithm *string + + // The security patch passed to as the com.android.build.<type>.security_patch avb property. + // Replacement for the make variables BOOT_SECURITY_PATCH / INIT_BOOT_SECURITY_PATCH. + Security_patch *string +} + +type bootImageType int + +const ( + unsupported bootImageType = iota + boot + vendorBoot + initBoot +) + +func toBootImageType(ctx android.ModuleContext, bootImageType string) bootImageType { + switch bootImageType { + case "boot": + return boot + case "vendor_boot": + return vendorBoot + case "init_boot": + return initBoot + default: + ctx.ModuleErrorf("Unknown boot_image_type %s. Must be one of \"boot\", \"vendor_boot\", or \"init_boot\"", bootImageType) + } + return unsupported +} + +func (b bootImageType) String() string { + switch b { + case boot: + return "boot" + case vendorBoot: + return "vendor_boot" + case initBoot: + return "init_boot" + default: + panic("unknown boot image type") + } +} + +func (b bootImageType) isBoot() bool { + return b == boot +} + +func (b bootImageType) isVendorBoot() bool { + return b == vendorBoot +} + +func (b bootImageType) isInitBoot() bool { + return b == initBoot } // bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb. -func bootimgFactory() android.Module { +func BootimgFactory() android.Module { module := &bootimg{} module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) @@ -112,12 +174,40 @@ func (b *bootimg) partitionName() string { } func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { - vendor := proptools.Bool(b.properties.Vendor_boot) - unsignedOutput := b.buildBootImage(ctx, vendor) + b.bootImageType = toBootImageType(ctx, proptools.StringDefault(b.properties.Boot_image_type, "boot")) + if b.bootImageType == unsupported { + return + } + + kernelProp := proptools.String(b.properties.Kernel_prebuilt) + if b.bootImageType.isVendorBoot() && kernelProp != "" { + ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") + return + } + if b.bootImageType.isBoot() && kernelProp == "" { + ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel") + return + } + var kernel android.Path + if kernelProp != "" { + kernel = android.PathForModuleSrc(ctx, kernelProp) + } + + unsignedOutput := b.buildBootImage(ctx, kernel) output := unsignedOutput if proptools.Bool(b.properties.Use_avb) { - output = b.signImage(ctx, unsignedOutput) + // This bootimg module supports 2 modes of avb signing, it picks between them based on + // if the private key is specified or not. If there is a key, it does a signing process + // similar to how the regular partitions (system, product, vendor, etc) are signed. + // If the key is not provided, it will just add an avb footer to the image. The avb + // footer only signing is how the make-built init_boot, boot, and vendor_boot images are + // built. + if proptools.String(b.properties.Avb_private_key) != "" { + output = b.signImage(ctx, unsignedOutput) + } else { + output = b.addAvbFooter(ctx, unsignedOutput, kernel) + } } b.installDir = android.PathForModuleInstall(ctx, "etc") @@ -127,23 +217,20 @@ func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { b.output = output } -func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.Path { +func (b *bootimg) buildBootImage(ctx android.ModuleContext, kernel android.Path) android.Path { output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()) builder := android.NewRuleBuilder(pctx, ctx) cmd := builder.Command().BuiltTool("mkbootimg") - kernel := proptools.String(b.properties.Kernel_prebuilt) - if vendor && kernel != "" { - ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") - return output - } - if !vendor && kernel == "" { - ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel") - return output + if kernel != nil { + cmd.FlagWithInput("--kernel ", kernel) } - if kernel != "" { - cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel)) + + // These arguments are passed for boot.img and init_boot.img generation + if b.bootImageType.isBoot() || b.bootImageType.isInitBoot() { + cmd.FlagWithArg("--os_version ", ctx.Config().PlatformVersionLastStable()) + cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch()) } dtbName := proptools.String(b.properties.Dtb_prebuilt) @@ -155,7 +242,7 @@ func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android cmdline := strings.Join(b.properties.Cmdline, " ") if cmdline != "" { flag := "--cmdline " - if vendor { + if b.bootImageType.isVendorBoot() { flag = "--vendor_cmdline " } cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline)) @@ -182,7 +269,7 @@ func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep) if filesystem, ok := ramdisk.(*filesystem); ok { flag := "--ramdisk " - if vendor { + if b.bootImageType.isVendorBoot() { flag = "--vendor_ramdisk " } cmd.FlagWithInput(flag, filesystem.OutputPath()) @@ -194,7 +281,7 @@ func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android bootconfig := proptools.String(b.properties.Bootconfig) if bootconfig != "" { - if !vendor { + if !b.bootImageType.isVendorBoot() { ctx.PropertyErrorf("bootconfig", "requires vendor_boot: true") return output } @@ -205,16 +292,60 @@ func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android cmd.FlagWithInput("--vendor_bootconfig ", android.PathForModuleSrc(ctx, bootconfig)) } + // Output flag for boot.img and init_boot.img flag := "--output " - if vendor { + if b.bootImageType.isVendorBoot() { flag = "--vendor_boot " } cmd.FlagWithOutput(flag, output) + if b.properties.Partition_size != nil { + assertMaxImageSize(builder, output, *b.properties.Partition_size, proptools.Bool(b.properties.Use_avb)) + } + builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName())) return output } +func (b *bootimg) addAvbFooter(ctx android.ModuleContext, unsignedImage android.Path, kernel android.Path) android.Path { + output := android.PathForModuleOut(ctx, b.installFileName()) + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command().Text("cp").Input(unsignedImage).Output(output) + cmd := builder.Command().BuiltTool("avbtool"). + Text("add_hash_footer"). + FlagWithInput("--image ", output) + + if b.properties.Partition_size != nil { + cmd.FlagWithArg("--partition_size ", strconv.FormatInt(*b.properties.Partition_size, 10)) + } else { + cmd.Flag("--dynamic_partition_size") + } + + if kernel != nil { + cmd.Textf(`--salt $(sha256sum "%s" | cut -d " " -f 1)`, kernel.String()) + cmd.Implicit(kernel) + } + + cmd.FlagWithArg("--partition_name ", b.bootImageType.String()) + + if !b.bootImageType.isVendorBoot() { + cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf( + "com.android.build.%s.os_version:%s", b.bootImageType.String(), ctx.Config().PlatformVersionLastStable()))) + } + + fingerprintFile := ctx.Config().BuildFingerprintFile(ctx) + cmd.FlagWithArg("--prop ", fmt.Sprintf("com.android.build.%s.fingerprint:%s", b.bootImageType.String(), fingerprintFile.String())) + cmd.OrderOnly(fingerprintFile) + + if b.properties.Security_patch != nil { + cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf( + "com.android.build.%s.security_patch:%s", b.bootImageType.String(), *b.properties.Security_patch))) + } + + builder.Build("add_avb_footer", fmt.Sprintf("Adding avb footer to %s", b.BaseModuleName())) + return output +} + func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.Path) android.Path { propFile, toolDeps := b.buildPropFile(ctx) diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index c34677060..dadacae3d 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -162,6 +162,10 @@ type FilesystemProperties struct { // Determines if the module is auto-generated from Soong or not. If the module is // auto-generated, its deps are exempted from visibility enforcement. Is_auto_generated *bool + + // Path to the dev nodes description file. This is only needed for building the ramdisk + // partition and should not be explicitly specified. + Dev_nodes_description_file *string `android:"path" blueprint:"mutated"` } // Additional properties required to generate erofs FS partitions. @@ -210,6 +214,10 @@ func initFilesystemModule(module android.DefaultableModule, filesystemModule *fi filesystemModule.PackagingBase.AllowHighPriorityDeps = true android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) + + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + filesystemModule.setDevNodesDescriptionProp() + }) } type depTag struct { @@ -229,6 +237,16 @@ func (t depTagWithVisibilityEnforcementBypass) ExcludeFromVisibilityEnforcement( var dependencyTagWithVisibilityEnforcementBypass = depTagWithVisibilityEnforcementBypass{} +// ramdiskDevNodesDescription is the name of the filegroup module that provides the file that +// contains the description of dev nodes added to the CPIO archive for the ramdisk partition. +const ramdiskDevNodesDescription = "ramdisk_node_list" + +func (f *filesystem) setDevNodesDescriptionProp() { + if proptools.String(f.properties.Partition_name) == "ramdisk" { + f.properties.Dev_nodes_description_file = proptools.StringPtr(":" + ramdiskDevNodesDescription) + } +} + func (f *filesystem) DepsMutator(ctx android.BottomUpMutatorContext) { if proptools.Bool(f.properties.Is_auto_generated) { f.AddDeps(ctx, dependencyTagWithVisibilityEnforcementBypass) @@ -659,6 +677,9 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) cmd := builder.Command(). BuiltTool("mkbootfs"). Text(rootDir.String()) // input directory + if nodeList := f.properties.Dev_nodes_description_file; nodeList != nil { + cmd.FlagWithInput("-n ", android.PathForModuleSrc(ctx, proptools.String(nodeList))) + } if compressed { cmd.Text("|"). BuiltTool("lz4"). @@ -689,6 +710,7 @@ var validPartitions = []string{ "odm_dlkm", "system_dlkm", "ramdisk", + "vendor_ramdisk", } func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) { @@ -904,3 +926,26 @@ func (f *filesystem) getLibsForLinkerConfig(ctx android.ModuleContext) ([]androi return provideModules, requireModules } + +// Checks that the given file doesn't exceed the given size, and will also print a warning +// if it's nearing the maximum size. Equivalent to assert-max-image-size in make: +// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/definitions.mk;l=3455;drc=993c4de29a02a6accd60ceaaee153307e1a18d10 +func assertMaxImageSize(builder *android.RuleBuilder, image android.Path, maxSize int64, addAvbLater bool) { + if addAvbLater { + // The value 69632 is derived from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in avbtool. + // Logic copied from make: + // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=228;drc=a6a0007ef24e16c0b79f439beac4a118416717e6 + maxSize -= 69632 + } + cmd := builder.Command() + cmd.Textf(`file="%s"; maxsize="%d";`+ + `total=$(stat -c "%%s" "$file" | tr -d '\n');`+ + `if [ "$total" -gt "$maxsize" ]; then `+ + ` echo "error: $file too large ($total > $maxsize)";`+ + ` false;`+ + `elif [ "$total" -gt $((maxsize - 32768)) ]; then `+ + ` echo "WARNING: $file approaching size limit ($total now; limit $maxsize)";`+ + `fi`, + image, maxSize) + cmd.Implicit(image) +} diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index f325d96ef..746e4de74 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -666,7 +666,9 @@ func TestUseSharedVariationOfNativeLib(t *testing.T) { partition := result.ModuleForTests("myfilesystem", "android_common") fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList")) - android.AssertDeepEquals(t, "cc_library listed in deps", "lib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo.so\nlib64/libm.so\n", fileList) + android.AssertDeepEquals(t, "cc_library listed in deps", + "lib64/bootstrap/libc.so\nlib64/bootstrap/libdl.so\nlib64/bootstrap/libm.so\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo.so\nlib64/libm.so\n", + fileList) } // binfoo1 overrides binbar. transitive deps of binbar should not be installed. @@ -701,7 +703,9 @@ cc_library { partition := result.ModuleForTests("myfilesystem", "android_common") fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList")) - android.AssertDeepEquals(t, "Shared library dep of overridden binary should not be installed", fileList, "bin/binfoo1\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo2.so\nlib64/libm.so\n") + android.AssertDeepEquals(t, "Shared library dep of overridden binary should not be installed", + "bin/binfoo1\nlib64/bootstrap/libc.so\nlib64/bootstrap/libdl.so\nlib64/bootstrap/libm.so\nlib64/libc++.so\nlib64/libc.so\nlib64/libdl.so\nlib64/libfoo2.so\nlib64/libm.so\n", + fileList) } func TestInstallLinkerConfigFile(t *testing.T) { @@ -754,3 +758,28 @@ func TestOverrideModulesInDeps(t *testing.T) { fileList := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("fileList")) android.AssertStringEquals(t, "filesystem with override app", "app/myoverrideapp/myoverrideapp.apk\n", fileList) } + +func TestRamdiskPartitionSetsDevNodes(t *testing.T) { + result := android.GroupFixturePreparers( + fixture, + android.FixtureMergeMockFs(android.MockFS{ + "ramdisk_node_list": nil, + }), + ).RunTestWithBp(t, ` + android_filesystem { + name: "ramdisk_filesystem", + partition_name: "ramdisk", + } + filegroup { + name: "ramdisk_node_list", + srcs: ["ramdisk_node_list"], + } + `) + + android.AssertBoolEquals( + t, + "Generated ramdisk image expected to depend on \"ramdisk_node_list\" module", + true, + java.CheckModuleHasDependency(t, result.TestContext, "ramdisk_filesystem", "android_common", "ramdisk_node_list"), + ) +} |