// 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 kernel import ( "fmt" "path/filepath" "strings" "android/soong/android" _ "android/soong/cc/config" "github.com/google/blueprint" "github.com/google/blueprint/proptools" ) func init() { pctx.Import("android/soong/cc/config") registerKernelBuildComponents(android.InitRegistrationContext) } func registerKernelBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_kernel_modules", PrebuiltKernelModulesFactory) } type prebuiltKernelModules struct { android.ModuleBase properties prebuiltKernelModulesProperties installDir android.InstallPath } type prebuiltKernelModulesProperties struct { // List or filegroup of prebuilt kernel module files. Should have .ko suffix. Srcs []string `android:"path,arch_variant"` // List of system_dlkm kernel modules that the local kernel modules depend on. // The deps will be assembled into intermediates directory for running depmod // but will not be added to the current module's installed files. System_deps []string `android:"path,arch_variant"` // If false, then srcs will not be included in modules.load. // This feature is used by system_dlkm Load_by_default *bool Blocklist_file *string `android:"path"` // Path to the kernel module options file Options_file *string `android:"path"` // Kernel version that these modules are for. Kernel modules are installed to // /lib/modules/ directory in the corresponding partition. Default is "". Kernel_version *string // Whether this module is directly installable to one of the partitions. Default is true Installable *bool } // prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory. // In addition, this module builds modules.load, modules.dep, modules.softdep and modules.alias // using depmod and installs them as well. func PrebuiltKernelModulesFactory() android.Module { module := &prebuiltKernelModules{} module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) return module } func (pkm *prebuiltKernelModules) installable() bool { return proptools.BoolDefault(pkm.properties.Installable, true) } func (pkm *prebuiltKernelModules) KernelVersion() string { return proptools.StringDefault(pkm.properties.Kernel_version, "") } func (pkm *prebuiltKernelModules) DepsMutator(ctx android.BottomUpMutatorContext) { // do nothing } func (pkm *prebuiltKernelModules) GenerateAndroidBuildActions(ctx android.ModuleContext) { if !pkm.installable() { pkm.SkipInstall() } modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs) systemModules := android.PathsForModuleSrc(ctx, pkm.properties.System_deps) depmodOut := pkm.runDepmod(ctx, modules, systemModules) strippedModules := stripDebugSymbols(ctx, modules) installDir := android.PathForModuleInstall(ctx, "lib", "modules") // Kernel module is installed to vendor_ramdisk/lib/modules regardless of product // configuration. This matches the behavior in make and prevents the files from being // installed in `vendor_ramdisk/first_stage_ramdisk`. if pkm.InstallInVendorRamdisk() { installDir = android.PathForModuleInPartitionInstall(ctx, "vendor_ramdisk", "lib", "modules") } if pkm.KernelVersion() != "" { installDir = installDir.Join(ctx, pkm.KernelVersion()) } for _, m := range strippedModules { ctx.InstallFile(installDir, filepath.Base(m.String()), m) } ctx.InstallFile(installDir, "modules.load", depmodOut.modulesLoad) ctx.InstallFile(installDir, "modules.dep", depmodOut.modulesDep) ctx.InstallFile(installDir, "modules.softdep", depmodOut.modulesSoftdep) ctx.InstallFile(installDir, "modules.alias", depmodOut.modulesAlias) pkm.installBlocklistFile(ctx, installDir) pkm.installOptionsFile(ctx, installDir) ctx.SetOutputFiles(modules, ".modules") } func (pkm *prebuiltKernelModules) installBlocklistFile(ctx android.ModuleContext, installDir android.InstallPath) { if pkm.properties.Blocklist_file == nil { return } blocklistOut := android.PathForModuleOut(ctx, "modules.blocklist") ctx.Build(pctx, android.BuildParams{ Rule: processBlocklistFile, Input: android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Blocklist_file)), Output: blocklistOut, }) ctx.InstallFile(installDir, "modules.blocklist", blocklistOut) } func (pkm *prebuiltKernelModules) installOptionsFile(ctx android.ModuleContext, installDir android.InstallPath) { if pkm.properties.Options_file == nil { return } optionsOut := android.PathForModuleOut(ctx, "modules.options") ctx.Build(pctx, android.BuildParams{ Rule: processOptionsFile, Input: android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Options_file)), Output: optionsOut, }) ctx.InstallFile(installDir, "modules.options", optionsOut) } var ( pctx = android.NewPackageContext("android/soong/kernel") stripRule = pctx.AndroidStaticRule("strip", blueprint.RuleParams{ Command: "$stripCmd -o $out --strip-debug $in", CommandDeps: []string{"$stripCmd"}, }, "stripCmd") ) func stripDebugSymbols(ctx android.ModuleContext, modules android.Paths) android.OutputPaths { dir := android.PathForModuleOut(ctx, "stripped").OutputPath var outputs android.OutputPaths for _, m := range modules { stripped := dir.Join(ctx, filepath.Base(m.String())) ctx.Build(pctx, android.BuildParams{ Rule: stripRule, Input: m, Output: stripped, Args: map[string]string{ "stripCmd": "${config.ClangBin}/llvm-strip", }, }) outputs = append(outputs, stripped) } return outputs } type depmodOutputs struct { modulesLoad android.OutputPath modulesDep android.OutputPath modulesSoftdep android.OutputPath modulesAlias android.OutputPath } var ( // system/lib/modules/foo.ko: system/lib/modules/bar.ko // will be converted to // /system/lib/modules/foo.ko: /system/lib/modules/bar.ko addLeadingSlashToPaths = pctx.AndroidStaticRule("add_leading_slash", blueprint.RuleParams{ Command: `sed -e 's|\([^: ]*lib/modules/[^: ]*\)|/\1|g' $in > $out`, }, ) // Remove empty lines. Raise an exception if line is _not_ formatted as `blocklist $name.ko` processBlocklistFile = pctx.AndroidStaticRule("process_blocklist_file", blueprint.RuleParams{ Command: `rm -rf $out && awk <$in > $out` + ` '/^#/ { print; next }` + ` NF == 0 { next }` + ` NF != 2 || $$1 != "blocklist"` + ` { print "Invalid blocklist line " FNR ": " $$0 >"/dev/stderr";` + ` exit_status = 1; next }` + ` { $$1 = $$1; print }` + ` END { exit exit_status }'`, }, ) // Remove empty lines. Raise an exception if line is _not_ formatted as `options $name.ko` processOptionsFile = pctx.AndroidStaticRule("process_options_file", blueprint.RuleParams{ Command: `rm -rf $out && awk <$in > $out` + ` '/^#/ { print; next }` + ` NF == 0 { next }` + ` NF < 2 || $$1 != "options"` + ` { print "Invalid options line " FNR ": " $$0 >"/dev/stderr";` + ` exit_status = 1; next }` + ` { $$1 = $$1; print }` + ` END { exit exit_status }'`, }, ) ) // This is the path in soong intermediates where the .ko files will be copied. // The layout should match the layout on device so that depmod can create meaningful modules.* files. func modulesDirForAndroidDlkm(ctx android.ModuleContext, modulesDir android.OutputPath, system bool) android.OutputPath { if ctx.InstallInSystemDlkm() || system { // The first component can be either system or system_dlkm // system works because /system/lib/modules is a symlink to /system_dlkm/lib/modules. // system was chosen to match the contents of the kati built modules.dep return modulesDir.Join(ctx, "system", "lib", "modules") } else if ctx.InstallInVendorDlkm() { return modulesDir.Join(ctx, "vendor", "lib", "modules") } else if ctx.InstallInOdmDlkm() { return modulesDir.Join(ctx, "odm", "lib", "modules") } else { // not an android dlkm module. return modulesDir } } func (pkm *prebuiltKernelModules) runDepmod(ctx android.ModuleContext, modules android.Paths, systemModules android.Paths) depmodOutputs { baseDir := android.PathForModuleOut(ctx, "depmod").OutputPath fakeVer := "0.0" // depmod demands this anyway modulesDir := baseDir.Join(ctx, "lib", "modules", fakeVer) modulesCpDir := modulesDirForAndroidDlkm(ctx, modulesDir, false) builder := android.NewRuleBuilder(pctx, ctx) // Copy the module files to a temporary dir builder.Command().Text("rm").Flag("-rf").Text(modulesCpDir.String()) builder.Command().Text("mkdir").Flag("-p").Text(modulesCpDir.String()) for _, m := range modules { builder.Command().Text("cp").Input(m).Text(modulesCpDir.String()) } modulesDirForSystemDlkm := modulesDirForAndroidDlkm(ctx, modulesDir, true) if len(systemModules) > 0 { builder.Command().Text("mkdir").Flag("-p").Text(modulesDirForSystemDlkm.String()) } for _, m := range systemModules { builder.Command().Text("cp").Input(m).Text(modulesDirForSystemDlkm.String()) } // Enumerate modules to load modulesLoad := modulesDir.Join(ctx, "modules.load") // If Load_by_default is set to false explicitly, create an empty modules.load if pkm.properties.Load_by_default != nil && !*pkm.properties.Load_by_default { builder.Command().Text("rm").Flag("-rf").Text(modulesLoad.String()) builder.Command().Text("touch").Output(modulesLoad) } else { var basenames []string for _, m := range modules { basenames = append(basenames, filepath.Base(m.String())) } builder.Command(). Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\""). Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\""). Text(">").Output(modulesLoad) } // Run depmod to build modules.dep/softdep/alias files modulesDep := modulesDir.Join(ctx, "modules.dep") modulesSoftdep := modulesDir.Join(ctx, "modules.softdep") modulesAlias := modulesDir.Join(ctx, "modules.alias") builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String()) builder.Command(). BuiltTool("depmod"). FlagWithArg("-b ", baseDir.String()). Text(fakeVer). ImplicitOutput(modulesDep). ImplicitOutput(modulesSoftdep). ImplicitOutput(modulesAlias) builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName())) finalModulesDep := modulesDep // Add a leading slash to paths in modules.dep of android dlkm if ctx.InstallInSystemDlkm() || ctx.InstallInVendorDlkm() || ctx.InstallInOdmDlkm() { finalModulesDep := modulesDep.ReplaceExtension(ctx, "intermediates") ctx.Build(pctx, android.BuildParams{ Rule: addLeadingSlashToPaths, Input: modulesDep, Output: finalModulesDep, }) } return depmodOutputs{modulesLoad, finalModulesDep, modulesSoftdep, modulesAlias} }