diff options
-rw-r--r-- | init/Android.bp | 1 | ||||
-rw-r--r-- | init/modalias_handler.cpp | 163 | ||||
-rw-r--r-- | init/modalias_handler.h | 48 | ||||
-rw-r--r-- | init/uevent.h | 1 | ||||
-rw-r--r-- | init/uevent_listener.cpp | 4 | ||||
-rw-r--r-- | init/ueventd.cpp | 13 |
6 files changed, 227 insertions, 3 deletions
diff --git a/init/Android.bp b/init/Android.bp index c02b8d187..660d58676 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -108,6 +108,7 @@ cc_library_static { "import_parser.cpp", "init.cpp", "keychords.cpp", + "modalias_handler.cpp", "parser.cpp", "persistent_properties.cpp", "persistent_properties.proto", diff --git a/init/modalias_handler.cpp b/init/modalias_handler.cpp new file mode 100644 index 000000000..1734a7e0f --- /dev/null +++ b/init/modalias_handler.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "modalias_handler.h" + +#include <fnmatch.h> +#include <sys/syscall.h> + +#include <algorithm> +#include <functional> +#include <string> +#include <vector> + +#include <android-base/chrono_utils.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> + +#include "parser.h" + +namespace android { +namespace init { + +Result<Success> ModaliasHandler::ParseDepCallback(std::vector<std::string>&& args) { + std::vector<std::string> deps; + + // Set first item as our modules path + std::string::size_type pos = args[0].find(':'); + if (pos != std::string::npos) { + deps.emplace_back(args[0].substr(0, pos)); + } else { + return Error() << "dependency lines must start with name followed by ':'"; + } + + // Remaining items are dependencies of our module + for (auto arg = args.begin() + 1; arg != args.end(); ++arg) { + deps.push_back(*arg); + } + + // Key is striped module name to match names in alias file + std::size_t start = args[0].find_last_of("/"); + std::size_t end = args[0].find(".ko:"); + if ((end - start) <= 1) return Error() << "malformed dependency line"; + auto mod_name = args[0].substr(start + 1, (end - start) - 1); + // module names can have '-', but their file names will have '_' + std::replace(mod_name.begin(), mod_name.end(), '-', '_'); + this->module_deps_[mod_name] = deps; + + return Success(); +} + +Result<Success> ModaliasHandler::ParseAliasCallback(std::vector<std::string>&& args) { + auto it = args.begin(); + const std::string& type = *it++; + + if (type != "alias") { + return Error() << "we only handle alias lines, got: " << type; + } + + if (args.size() != 3) { + return Error() << "alias lines must have 3 entries"; + } + + std::string& alias = *it++; + std::string& module_name = *it++; + this->module_aliases_.emplace_back(alias, module_name); + + return Success(); +} + +ModaliasHandler::ModaliasHandler() { + using namespace std::placeholders; + + static const std::string base_paths[] = { + "/vendor/lib/modules/", + "/lib/modules/", + "/odm/lib/modules/", + }; + + Parser alias_parser; + auto alias_callback = std::bind(&ModaliasHandler::ParseAliasCallback, this, _1); + alias_parser.AddSingleLineParser("alias", alias_callback); + for (const auto& base_path : base_paths) alias_parser.ParseConfig(base_path + "modules.alias"); + + Parser dep_parser; + auto dep_callback = std::bind(&ModaliasHandler::ParseDepCallback, this, _1); + dep_parser.AddSingleLineParser("", dep_callback); + for (const auto& base_path : base_paths) dep_parser.ParseConfig(base_path + "modules.dep"); +} + +Result<Success> ModaliasHandler::Insmod(const std::string& path_name, const std::string& args) { + base::unique_fd fd( + TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC))); + if (fd == -1) return ErrnoError() << "Could not open module '" << path_name << "'"; + + int ret = syscall(__NR_finit_module, fd.get(), args.c_str(), 0); + if (ret != 0) { + if (errno == EEXIST) { + // Module already loaded + return Success(); + } + return ErrnoError() << "Failed to insmod '" << path_name << "' with args '" << args << "'"; + } + + LOG(INFO) << "Loaded kernel module " << path_name; + return Success(); +} + +Result<Success> ModaliasHandler::InsmodWithDeps(const std::string& module_name, + const std::string& args) { + if (module_name.empty()) { + return Error() << "Need valid module name"; + } + + auto it = module_deps_.find(module_name); + if (it == module_deps_.end()) { + return Error() << "Module '" << module_name << "' not in dependency file"; + } + auto& dependencies = it->second; + + // load module dependencies in reverse order + for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) { + if (auto result = Insmod(*dep, ""); !result) return result; + } + + // load target module itself with args + return Insmod(dependencies[0], args); +} + +void ModaliasHandler::HandleModaliasEvent(const Uevent& uevent) { + if (uevent.modalias.empty()) return; + + for (const auto& [alias, module] : module_aliases_) { + if (fnmatch(alias.c_str(), uevent.modalias.c_str(), 0) != 0) continue; // Keep looking + + LOG(DEBUG) << "Loading kernel module '" << module << "' for alias '" << uevent.modalias + << "'"; + + if (auto result = InsmodWithDeps(module, ""); !result) { + LOG(ERROR) << "Cannot load module: " << result.error(); + // try another one since there may be another match + continue; + } + + // loading was successful + return; + } +} + +} // namespace init +} // namespace android diff --git a/init/modalias_handler.h b/init/modalias_handler.h new file mode 100644 index 000000000..e79da3275 --- /dev/null +++ b/init/modalias_handler.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +#include "result.h" +#include "uevent.h" + +#include <string> +#include <unordered_map> +#include <vector> + +namespace android { +namespace init { + +class ModaliasHandler { + public: + ModaliasHandler(); + ~ModaliasHandler(){}; + + void HandleModaliasEvent(const Uevent& uevent); + + private: + Result<Success> InsmodWithDeps(const std::string& module_name, const std::string& args); + Result<Success> Insmod(const std::string& path_name, const std::string& args); + + Result<Success> ParseDepCallback(std::vector<std::string>&& args); + Result<Success> ParseAliasCallback(std::vector<std::string>&& args); + + std::vector<std::pair<std::string, std::string>> module_aliases_; + std::unordered_map<std::string, std::vector<std::string>> module_deps_; +}; + +} // namespace init +} // namespace android diff --git a/init/uevent.h b/init/uevent.h index c4fd9454c..dc35fd968 100644 --- a/init/uevent.h +++ b/init/uevent.h @@ -29,6 +29,7 @@ struct Uevent { std::string firmware; std::string partition_name; std::string device_name; + std::string modalias; int partition_num; int major; int minor; diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp index 24b14c44d..8cf212867 100644 --- a/init/uevent_listener.cpp +++ b/init/uevent_listener.cpp @@ -39,6 +39,7 @@ static void ParseEvent(const char* msg, Uevent* uevent) { uevent->firmware.clear(); uevent->partition_name.clear(); uevent->device_name.clear(); + uevent->modalias.clear(); // currently ignoring SEQNUM while (*msg) { if (!strncmp(msg, "ACTION=", 7)) { @@ -68,6 +69,9 @@ static void ParseEvent(const char* msg, Uevent* uevent) { } else if (!strncmp(msg, "DEVNAME=", 8)) { msg += 8; uevent->device_name = msg; + } else if (!strncmp(msg, "MODALIAS=", 9)) { + msg += 9; + uevent->modalias = msg; } // advance to after the next \0 diff --git a/init/ueventd.cpp b/init/ueventd.cpp index cd45a3fb7..e9d829b51 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp @@ -36,6 +36,7 @@ #include "devices.h" #include "firmware_handler.h" +#include "modalias_handler.h" #include "selinux.h" #include "uevent_listener.h" #include "ueventd_parser.h" @@ -106,9 +107,11 @@ namespace init { class ColdBoot { public: - ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler) + ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler, + ModaliasHandler& modalias_handler) : uevent_listener_(uevent_listener), device_handler_(device_handler), + modalias_handler_(modalias_handler), num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {} void Run(); @@ -122,6 +125,7 @@ class ColdBoot { UeventListener& uevent_listener_; DeviceHandler& device_handler_; + ModaliasHandler& modalias_handler_; unsigned int num_handler_subprocesses_; std::vector<Uevent> uevent_queue_; @@ -133,6 +137,7 @@ void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_pr for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) { auto& uevent = uevent_queue_[i]; device_handler_.HandleDeviceEvent(uevent); + modalias_handler_.HandleModaliasEvent(uevent); } _exit(EXIT_SUCCESS); } @@ -230,6 +235,7 @@ int ueventd_main(int argc, char** argv) { SelabelInitialize(); DeviceHandler device_handler; + ModaliasHandler modalias_handler; UeventListener uevent_listener; { @@ -251,7 +257,7 @@ int ueventd_main(int argc, char** argv) { } if (access(COLDBOOT_DONE, F_OK) != 0) { - ColdBoot cold_boot(uevent_listener, device_handler); + ColdBoot cold_boot(uevent_listener, device_handler, modalias_handler); cold_boot.Run(); } @@ -262,8 +268,9 @@ int ueventd_main(int argc, char** argv) { while (waitpid(-1, nullptr, WNOHANG) > 0) { } - uevent_listener.Poll([&device_handler](const Uevent& uevent) { + uevent_listener.Poll([&device_handler, &modalias_handler](const Uevent& uevent) { HandleFirmwareEvent(uevent); + modalias_handler.HandleModaliasEvent(uevent); device_handler.HandleDeviceEvent(uevent); return ListenerAction::kContinue; }); |