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