ueventd: scans /apex/*/firmware for firmwares
In addition to "firmware_directories", ueventd scans /apex/*/firmware/
directory as well to find firmware files.
Bug: 167942098
Test: loading firmware from vibrator apex successfully.
(sunfish)
Change-Id: I90fc8f9ad843a08b1ca98a2be1b5d22c0c5954a3
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 053ebf8..0f584b2 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -86,6 +86,8 @@
for a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the
kernel.
+`/apex/*/firmware` is also searched after a list of firmware directories.
+
The list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc
file. This line takes the format of
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index dff7b69..b50b4ef 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -17,6 +17,7 @@
#include "firmware_handler.h"
#include <fcntl.h>
+#include <glob.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
@@ -30,6 +31,7 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -203,25 +205,28 @@
}
std::vector<std::string> attempted_paths_and_errors;
-
- int booting = IsBooting();
-try_loading_again:
- attempted_paths_and_errors.clear();
- for (const auto& firmware_directory : firmware_directories_) {
+ auto TryLoadFirmware = [&](const std::string& firmware_directory) {
std::string file = firmware_directory + firmware;
unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
if (fw_fd == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", open failed: " + strerror(errno));
- continue;
+ return false;
}
struct stat sb;
if (fstat(fw_fd, &sb) == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", fstat failed: " + strerror(errno));
- continue;
+ return false;
}
LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
+ return true;
+ };
+
+ int booting = IsBooting();
+try_loading_again:
+ attempted_paths_and_errors.clear();
+ if (ForEachFirmwareDirectory(TryLoadFirmware)) {
return;
}
@@ -242,6 +247,33 @@
write(loading_fd, "-1", 2);
}
+bool FirmwareHandler::ForEachFirmwareDirectory(
+ std::function<bool(const std::string&)> handler) const {
+ for (const std::string& firmware_directory : firmware_directories_) {
+ if (std::invoke(handler, firmware_directory)) {
+ return true;
+ }
+ }
+
+ glob_t glob_result;
+ glob("/apex/*/firmware/", GLOB_MARK, nullptr, &glob_result);
+ auto free_glob = android::base::make_scope_guard(std::bind(&globfree, &glob_result));
+ for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+ char* apex_firmware_directory = glob_result.gl_pathv[i];
+ // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will look into the
+ // same apex twice.
+ if (strchr(apex_firmware_directory, '@')) {
+ continue;
+ }
+ if (std::invoke(handler, apex_firmware_directory)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void FirmwareHandler::HandleUevent(const Uevent& uevent) {
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index b4138f1..8b758ae 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -18,6 +18,7 @@
#include <pwd.h>
+#include <functional>
#include <string>
#include <vector>
@@ -52,6 +53,7 @@
const Uevent& uevent) const;
std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
+ bool ForEachFirmwareDirectory(std::function<bool(const std::string&)> handler) const;
std::vector<std::string> firmware_directories_;
std::vector<ExternalFirmwareHandler> external_firmware_handlers_;