1// Copyright (C) 2021 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 kernel 16 17import ( 18 "fmt" 19 "path/filepath" 20 "strings" 21 22 "android/soong/android" 23 _ "android/soong/cc/config" 24 25 "github.com/google/blueprint" 26 "github.com/google/blueprint/proptools" 27) 28 29func init() { 30 pctx.Import("android/soong/cc/config") 31 registerKernelBuildComponents(android.InitRegistrationContext) 32} 33 34func registerKernelBuildComponents(ctx android.RegistrationContext) { 35 ctx.RegisterModuleType("prebuilt_kernel_modules", PrebuiltKernelModulesFactory) 36} 37 38type prebuiltKernelModules struct { 39 android.ModuleBase 40 41 properties prebuiltKernelModulesProperties 42 43 installDir android.InstallPath 44} 45 46type prebuiltKernelModulesProperties struct { 47 // List or filegroup of prebuilt kernel module files. Should have .ko suffix. 48 Srcs []string `android:"path,arch_variant"` 49 50 // List of system_dlkm kernel modules that the local kernel modules depend on. 51 // The deps will be assembled into intermediates directory for running depmod 52 // but will not be added to the current module's installed files. 53 System_deps []string `android:"path,arch_variant"` 54 55 // If false, then srcs will not be included in modules.load. 56 // This feature is used by system_dlkm 57 Load_by_default *bool 58 59 Blocklist_file *string `android:"path"` 60 61 // Path to the kernel module options file 62 Options_file *string `android:"path"` 63 64 // Kernel version that these modules are for. Kernel modules are installed to 65 // /lib/modules/<kernel_version> directory in the corresponding partition. Default is "". 66 Kernel_version *string 67 68 // Whether this module is directly installable to one of the partitions. Default is true 69 Installable *bool 70} 71 72// prebuilt_kernel_modules installs a set of prebuilt kernel module files to the correct directory. 73// In addition, this module builds modules.load, modules.dep, modules.softdep and modules.alias 74// using depmod and installs them as well. 75func PrebuiltKernelModulesFactory() android.Module { 76 module := &prebuiltKernelModules{} 77 module.AddProperties(&module.properties) 78 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) 79 return module 80} 81 82func (pkm *prebuiltKernelModules) installable() bool { 83 return proptools.BoolDefault(pkm.properties.Installable, true) 84} 85 86func (pkm *prebuiltKernelModules) KernelVersion() string { 87 return proptools.StringDefault(pkm.properties.Kernel_version, "") 88} 89 90func (pkm *prebuiltKernelModules) DepsMutator(ctx android.BottomUpMutatorContext) { 91 // do nothing 92} 93 94func (pkm *prebuiltKernelModules) GenerateAndroidBuildActions(ctx android.ModuleContext) { 95 if !pkm.installable() { 96 pkm.SkipInstall() 97 } 98 99 modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs) 100 systemModules := android.PathsForModuleSrc(ctx, pkm.properties.System_deps) 101 102 depmodOut := pkm.runDepmod(ctx, modules, systemModules) 103 strippedModules := stripDebugSymbols(ctx, modules) 104 105 installDir := android.PathForModuleInstall(ctx, "lib", "modules") 106 // Kernel module is installed to vendor_ramdisk/lib/modules regardless of product 107 // configuration. This matches the behavior in make and prevents the files from being 108 // installed in `vendor_ramdisk/first_stage_ramdisk`. 109 if pkm.InstallInVendorRamdisk() { 110 installDir = android.PathForModuleInPartitionInstall(ctx, "vendor_ramdisk", "lib", "modules") 111 } 112 113 if pkm.KernelVersion() != "" { 114 installDir = installDir.Join(ctx, pkm.KernelVersion()) 115 } 116 117 for _, m := range strippedModules { 118 ctx.InstallFile(installDir, filepath.Base(m.String()), m) 119 } 120 ctx.InstallFile(installDir, "modules.load", depmodOut.modulesLoad) 121 ctx.InstallFile(installDir, "modules.dep", depmodOut.modulesDep) 122 ctx.InstallFile(installDir, "modules.softdep", depmodOut.modulesSoftdep) 123 ctx.InstallFile(installDir, "modules.alias", depmodOut.modulesAlias) 124 pkm.installBlocklistFile(ctx, installDir) 125 pkm.installOptionsFile(ctx, installDir) 126 127 ctx.SetOutputFiles(modules, ".modules") 128} 129 130func (pkm *prebuiltKernelModules) installBlocklistFile(ctx android.ModuleContext, installDir android.InstallPath) { 131 if pkm.properties.Blocklist_file == nil { 132 return 133 } 134 blocklistOut := android.PathForModuleOut(ctx, "modules.blocklist") 135 136 ctx.Build(pctx, android.BuildParams{ 137 Rule: processBlocklistFile, 138 Input: android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Blocklist_file)), 139 Output: blocklistOut, 140 }) 141 ctx.InstallFile(installDir, "modules.blocklist", blocklistOut) 142} 143 144func (pkm *prebuiltKernelModules) installOptionsFile(ctx android.ModuleContext, installDir android.InstallPath) { 145 if pkm.properties.Options_file == nil { 146 return 147 } 148 optionsOut := android.PathForModuleOut(ctx, "modules.options") 149 150 ctx.Build(pctx, android.BuildParams{ 151 Rule: processOptionsFile, 152 Input: android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Options_file)), 153 Output: optionsOut, 154 }) 155 ctx.InstallFile(installDir, "modules.options", optionsOut) 156} 157 158var ( 159 pctx = android.NewPackageContext("android/soong/kernel") 160 161 stripRule = pctx.AndroidStaticRule("strip", 162 blueprint.RuleParams{ 163 Command: "$stripCmd -o $out --strip-debug $in", 164 CommandDeps: []string{"$stripCmd"}, 165 }, "stripCmd") 166) 167 168func stripDebugSymbols(ctx android.ModuleContext, modules android.Paths) android.OutputPaths { 169 dir := android.PathForModuleOut(ctx, "stripped").OutputPath 170 var outputs android.OutputPaths 171 172 for _, m := range modules { 173 stripped := dir.Join(ctx, filepath.Base(m.String())) 174 ctx.Build(pctx, android.BuildParams{ 175 Rule: stripRule, 176 Input: m, 177 Output: stripped, 178 Args: map[string]string{ 179 "stripCmd": "${config.ClangBin}/llvm-strip", 180 }, 181 }) 182 outputs = append(outputs, stripped) 183 } 184 185 return outputs 186} 187 188type depmodOutputs struct { 189 modulesLoad android.OutputPath 190 modulesDep android.OutputPath 191 modulesSoftdep android.OutputPath 192 modulesAlias android.OutputPath 193} 194 195var ( 196 // system/lib/modules/foo.ko: system/lib/modules/bar.ko 197 // will be converted to 198 // /system/lib/modules/foo.ko: /system/lib/modules/bar.ko 199 addLeadingSlashToPaths = pctx.AndroidStaticRule("add_leading_slash", 200 blueprint.RuleParams{ 201 Command: `sed -e 's|\([^: ]*lib/modules/[^: ]*\)|/\1|g' $in > $out`, 202 }, 203 ) 204 // Remove empty lines. Raise an exception if line is _not_ formatted as `blocklist $name.ko` 205 processBlocklistFile = pctx.AndroidStaticRule("process_blocklist_file", 206 blueprint.RuleParams{ 207 Command: `rm -rf $out && awk <$in > $out` + 208 ` '/^#/ { print; next }` + 209 ` NF == 0 { next }` + 210 ` NF != 2 || $$1 != "blocklist"` + 211 ` { print "Invalid blocklist line " FNR ": " $$0 >"/dev/stderr";` + 212 ` exit_status = 1; next }` + 213 ` { $$1 = $$1; print }` + 214 ` END { exit exit_status }'`, 215 }, 216 ) 217 // Remove empty lines. Raise an exception if line is _not_ formatted as `options $name.ko` 218 processOptionsFile = pctx.AndroidStaticRule("process_options_file", 219 blueprint.RuleParams{ 220 Command: `rm -rf $out && awk <$in > $out` + 221 ` '/^#/ { print; next }` + 222 ` NF == 0 { next }` + 223 ` NF < 2 || $$1 != "options"` + 224 ` { print "Invalid options line " FNR ": " $$0 >"/dev/stderr";` + 225 ` exit_status = 1; next }` + 226 ` { $$1 = $$1; print }` + 227 ` END { exit exit_status }'`, 228 }, 229 ) 230) 231 232// This is the path in soong intermediates where the .ko files will be copied. 233// The layout should match the layout on device so that depmod can create meaningful modules.* files. 234func modulesDirForAndroidDlkm(ctx android.ModuleContext, modulesDir android.OutputPath, system bool) android.OutputPath { 235 if ctx.InstallInSystemDlkm() || system { 236 // The first component can be either system or system_dlkm 237 // system works because /system/lib/modules is a symlink to /system_dlkm/lib/modules. 238 // system was chosen to match the contents of the kati built modules.dep 239 return modulesDir.Join(ctx, "system", "lib", "modules") 240 } else if ctx.InstallInVendorDlkm() { 241 return modulesDir.Join(ctx, "vendor", "lib", "modules") 242 } else if ctx.InstallInOdmDlkm() { 243 return modulesDir.Join(ctx, "odm", "lib", "modules") 244 } else { 245 // not an android dlkm module. 246 return modulesDir 247 } 248} 249 250func (pkm *prebuiltKernelModules) runDepmod(ctx android.ModuleContext, modules android.Paths, systemModules android.Paths) depmodOutputs { 251 baseDir := android.PathForModuleOut(ctx, "depmod").OutputPath 252 fakeVer := "0.0" // depmod demands this anyway 253 modulesDir := baseDir.Join(ctx, "lib", "modules", fakeVer) 254 modulesCpDir := modulesDirForAndroidDlkm(ctx, modulesDir, false) 255 256 builder := android.NewRuleBuilder(pctx, ctx) 257 258 // Copy the module files to a temporary dir 259 builder.Command().Text("rm").Flag("-rf").Text(modulesCpDir.String()) 260 builder.Command().Text("mkdir").Flag("-p").Text(modulesCpDir.String()) 261 for _, m := range modules { 262 builder.Command().Text("cp").Input(m).Text(modulesCpDir.String()) 263 } 264 265 modulesDirForSystemDlkm := modulesDirForAndroidDlkm(ctx, modulesDir, true) 266 if len(systemModules) > 0 { 267 builder.Command().Text("mkdir").Flag("-p").Text(modulesDirForSystemDlkm.String()) 268 } 269 for _, m := range systemModules { 270 builder.Command().Text("cp").Input(m).Text(modulesDirForSystemDlkm.String()) 271 } 272 273 // Enumerate modules to load 274 modulesLoad := modulesDir.Join(ctx, "modules.load") 275 // If Load_by_default is set to false explicitly, create an empty modules.load 276 if pkm.properties.Load_by_default != nil && !*pkm.properties.Load_by_default { 277 builder.Command().Text("rm").Flag("-rf").Text(modulesLoad.String()) 278 builder.Command().Text("touch").Output(modulesLoad) 279 } else { 280 var basenames []string 281 for _, m := range modules { 282 basenames = append(basenames, filepath.Base(m.String())) 283 } 284 builder.Command(). 285 Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\""). 286 Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\""). 287 Text(">").Output(modulesLoad) 288 } 289 290 // Run depmod to build modules.dep/softdep/alias files 291 modulesDep := modulesDir.Join(ctx, "modules.dep") 292 modulesSoftdep := modulesDir.Join(ctx, "modules.softdep") 293 modulesAlias := modulesDir.Join(ctx, "modules.alias") 294 builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String()) 295 builder.Command(). 296 BuiltTool("depmod"). 297 FlagWithArg("-b ", baseDir.String()). 298 Text(fakeVer). 299 ImplicitOutput(modulesDep). 300 ImplicitOutput(modulesSoftdep). 301 ImplicitOutput(modulesAlias) 302 303 builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName())) 304 305 finalModulesDep := modulesDep 306 // Add a leading slash to paths in modules.dep of android dlkm 307 if ctx.InstallInSystemDlkm() || ctx.InstallInVendorDlkm() || ctx.InstallInOdmDlkm() { 308 finalModulesDep := modulesDep.ReplaceExtension(ctx, "intermediates") 309 ctx.Build(pctx, android.BuildParams{ 310 Rule: addLeadingSlashToPaths, 311 Input: modulesDep, 312 Output: finalModulesDep, 313 }) 314 } 315 316 return depmodOutputs{modulesLoad, finalModulesDep, modulesSoftdep, modulesAlias} 317} 318