first_stage_init: support kernel module directories

Kernel modules may be located within directories in /lib/modules.
Attempt to load kernel modules from each directory that has a name starting with
the major and minor version of the currently running kernel. If a single
kernel module is successfully loaded from a directory, that directory is
treated as the correct kernel module directory for the system. No other
kernel module directories are searched and any kernel module load errors
in that directory are fatal. If the attempt to load the first kernel
module from a directory fails, or if there are no kernel modules in a
directory, then the search proceeds to the next directory.

If no kernel module is successfully loaded from any directory as above,
an attempt is made to load kernel modules from the top level at
/lib/modules/.

Bug: 157645635
Change-Id: I92eadd8617f03a645da460ccb776bc04fa541f00
Merged-In: I92eadd8617f03a645da460ccb776bc04fa541f00
This commit is contained in:
Steve Muckle 2020-05-29 16:31:19 -07:00
parent 8bafa9e28e
commit e35d217958
1 changed files with 74 additions and 11 deletions

View File

@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <filesystem>
@ -99,6 +100,77 @@ bool ForceNormalBoot(const std::string& cmdline) {
} // namespace
std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
auto module_load_file = "modules.load";
if (recovery) {
struct stat fileStat;
std::string recovery_load_path = dir_path + "/modules.load.recovery";
if (!stat(recovery_load_path.c_str(), &fileStat)) {
module_load_file = "modules.load.recovery";
}
}
return module_load_file;
}
#define MODULE_BASE_DIR "/lib/modules"
bool LoadKernelModules(bool recovery, bool want_console) {
struct utsname uts;
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
}
int major, minor;
if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
LOG(FATAL) << "Failed to parse kernel version " << uts.release;
}
std::unique_ptr<DIR, decltype(&closedir)> base_dir(opendir(MODULE_BASE_DIR), closedir);
if (!base_dir) {
LOG(INFO) << "Unable to open /lib/modules, skipping module loading.";
return true;
}
dirent* entry;
std::vector<std::string> module_dirs;
while ((entry = readdir(base_dir.get()))) {
if (entry->d_type != DT_DIR) {
continue;
}
int dir_major, dir_minor;
if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
dir_minor != minor) {
continue;
}
module_dirs.emplace_back(entry->d_name);
}
// Sort the directories so they are iterated over during module loading
// in a consistent order. Alphabetical sorting is fine here because the
// kernel version at the beginning of the directory name must match the
// current kernel version, so the sort only applies to a label that
// follows the kernel version, for example /lib/modules/5.4 vs.
// /lib/modules/5.4-gki.
std::sort(module_dirs.begin(), module_dirs.end());
for (const auto& module_dir : module_dirs) {
std::string dir_path = MODULE_BASE_DIR "/";
dir_path.append(module_dir);
Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
bool retval = m.LoadListedModules(!want_console);
int modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
return retval;
}
}
Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
bool retval = m.LoadListedModules(!want_console);
int modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
return retval;
}
return true;
}
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@ -190,18 +262,9 @@ int FirstStageMain(int argc, char** argv) {
old_root_dir.reset();
}
std::string module_load_file = "modules.load";
if (IsRecoveryMode() && !ForceNormalBoot(cmdline)) {
struct stat fileStat;
std::string recovery_load_path = "/lib/modules/modules.load.recovery";
if (!stat(recovery_load_path.c_str(), &fileStat)) {
module_load_file = "modules.load.recovery";
}
}
Modprobe m({"/lib/modules"}, module_load_file);
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
if (!m.LoadListedModules(!want_console)) {
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {