xref: /aosp_15_r20/system/core/init/firmware_handler.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "firmware_handler.h"
18 
19 #include <fcntl.h>
20 #include <fnmatch.h>
21 #include <glob.h>
22 #include <grp.h>
23 #include <pwd.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/sendfile.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 
31 #include <thread>
32 
33 #include <android-base/chrono_utils.h>
34 #include <android-base/file.h>
35 #include <android-base/logging.h>
36 #include <android-base/properties.h>
37 #include <android-base/scopeguard.h>
38 #include <android-base/strings.h>
39 #include <android-base/unique_fd.h>
40 
41 #include "exthandler/exthandler.h"
42 
43 using android::base::ReadFdToString;
44 using android::base::Socketpair;
45 using android::base::Split;
46 using android::base::Timer;
47 using android::base::Trim;
48 using android::base::unique_fd;
49 using android::base::WaitForProperty;
50 using android::base::WriteFully;
51 
52 namespace android {
53 namespace init {
54 
55 namespace {
PrefixMatch(const std::string & pattern,const std::string & path)56 bool PrefixMatch(const std::string& pattern, const std::string& path) {
57     return android::base::StartsWith(path, pattern);
58 }
59 
FnMatch(const std::string & pattern,const std::string & path)60 bool FnMatch(const std::string& pattern, const std::string& path) {
61     return fnmatch(pattern.c_str(), path.c_str(), 0) == 0;
62 }
63 
EqualMatch(const std::string & pattern,const std::string & path)64 bool EqualMatch(const std::string& pattern, const std::string& path) {
65     return pattern == path;
66 }
67 }  // namespace
68 
LoadFirmware(const std::string & firmware,const std::string & root,int fw_fd,size_t fw_size,int loading_fd,int data_fd)69 static void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd,
70                          size_t fw_size, int loading_fd, int data_fd) {
71     // Start transfer.
72     WriteFully(loading_fd, "1", 1);
73 
74     // Copy the firmware.
75     int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
76     if (rc == -1) {
77         PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << firmware << "' }";
78     }
79 
80     // Tell the firmware whether to abort or commit.
81     const char* response = (rc != -1) ? "0" : "-1";
82     WriteFully(loading_fd, response, strlen(response));
83 }
84 
IsBooting()85 static bool IsBooting() {
86     return access("/dev/.booting", F_OK) == 0;
87 }
88 
IsApexActivated()89 static bool IsApexActivated() {
90     static bool apex_activated = []() {
91         // Wait for com.android.runtime.apex activation
92         // Property name and value must be kept in sync with system/apexd/apex/apex_constants.h
93         // 60s is the default firmware sysfs fallback timeout. (/sys/class/firmware/timeout)
94         if (!WaitForProperty("apexd.status", "activated", 60s)) {
95             LOG(ERROR) << "Apexd activation wait timeout";
96             return false;
97         }
98         return true;
99     }();
100 
101     return apex_activated;
102 }
103 
NeedsRerunExternalHandler()104 static bool NeedsRerunExternalHandler() {
105     static bool first = true;
106 
107     // Rerun external handler only on the first try and when apex is activated
108     if (first) {
109         first = false;
110         return IsApexActivated();
111     }
112 
113     return first;
114 }
115 
ExternalFirmwareHandler(std::string devpath,uid_t uid,gid_t gid,std::string handler_path)116 ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
117                                                  std::string handler_path)
118     : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
119     auto wildcard_position = this->devpath.find('*');
120     if (wildcard_position != std::string::npos) {
121         if (wildcard_position == this->devpath.length() - 1) {
122             this->devpath.pop_back();
123             match = std::bind(PrefixMatch, this->devpath, std::placeholders::_1);
124         } else {
125             match = std::bind(FnMatch, this->devpath, std::placeholders::_1);
126         }
127     } else {
128         match = std::bind(EqualMatch, this->devpath, std::placeholders::_1);
129     }
130 }
131 
ExternalFirmwareHandler(std::string devpath,uid_t uid,std::string handler_path)132 ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
133                                                  std::string handler_path)
134     : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}
135 
FirmwareHandler(std::vector<std::string> firmware_directories,std::vector<ExternalFirmwareHandler> external_firmware_handlers)136 FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
137                                  std::vector<ExternalFirmwareHandler> external_firmware_handlers)
138     : firmware_directories_(std::move(firmware_directories)),
139       external_firmware_handlers_(std::move(external_firmware_handlers)) {}
140 
GetFirmwarePath(const Uevent & uevent) const141 std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {
142     for (const auto& external_handler : external_firmware_handlers_) {
143         if (external_handler.match(uevent.path)) {
144             LOG(INFO) << "Launching external firmware handler '" << external_handler.handler_path
145                       << "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
146                       << "'";
147 
148             std::unordered_map<std::string, std::string> envs_map;
149             envs_map["FIRMWARE"] = uevent.firmware;
150             envs_map["DEVPATH"] = uevent.path;
151 
152             auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
153                                              external_handler.gid, envs_map);
154             if (!result.ok() && NeedsRerunExternalHandler()) {
155                 auto res = RunExternalHandler(external_handler.handler_path, external_handler.uid,
156                                               external_handler.gid, envs_map);
157                 result = std::move(res);
158             }
159             if (!result.ok()) {
160                 LOG(ERROR) << "Using default firmware; External firmware handler failed: "
161                            << result.error();
162                 return uevent.firmware;
163             }
164             if (result->find("..") != std::string::npos) {
165                 LOG(ERROR) << "Using default firmware; External firmware handler provided an "
166                               "invalid path, '"
167                            << *result << "'";
168                 return uevent.firmware;
169             }
170             LOG(INFO) << "Loading firmware '" << *result << "' in place of '" << uevent.firmware
171                       << "'";
172             return *result;
173         }
174     }
175     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
176     return uevent.firmware;
177 }
178 
ProcessFirmwareEvent(const std::string & path,const std::string & firmware) const179 void FirmwareHandler::ProcessFirmwareEvent(const std::string& path,
180                                            const std::string& firmware) const {
181     std::string root = "/sys" + path;
182     std::string loading = root + "/loading";
183     std::string data = root + "/data";
184 
185     unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
186     if (loading_fd == -1) {
187         PLOG(ERROR) << "couldn't open firmware loading fd for " << firmware;
188         return;
189     }
190 
191     unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
192     if (data_fd == -1) {
193         PLOG(ERROR) << "couldn't open firmware data fd for " << firmware;
194         return;
195     }
196 
197     std::vector<std::string> attempted_paths_and_errors;
198     auto TryLoadFirmware = [&](const std::string& firmware_directory) {
199         std::string file = firmware_directory + firmware;
200         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
201         if (fw_fd == -1) {
202             attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
203                                                     ", open failed: " + strerror(errno));
204             return false;
205         }
206         struct stat sb;
207         if (fstat(fw_fd.get(), &sb) == -1) {
208             attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
209                                                     ", fstat failed: " + strerror(errno));
210             return false;
211         }
212         LOG(INFO) << "found " << file << " for " << path;
213         LoadFirmware(firmware, root, fw_fd.get(), sb.st_size, loading_fd.get(), data_fd.get());
214         return true;
215     };
216 
217     int booting = IsBooting();
218 try_loading_again:
219     attempted_paths_and_errors.clear();
220     if (ForEachFirmwareDirectory(TryLoadFirmware)) {
221         return;
222     }
223 
224     if (booting) {
225         // If we're not fully booted, we may be missing
226         // filesystems needed for firmware, wait and retry.
227         std::this_thread::sleep_for(100ms);
228         booting = IsBooting();
229         goto try_loading_again;
230     }
231 
232     LOG(ERROR) << "firmware: could not find firmware for " << firmware;
233     for (const auto& message : attempted_paths_and_errors) {
234         LOG(ERROR) << message;
235     }
236 
237     // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
238     write(loading_fd.get(), "-1", 2);
239 }
240 
ForEachFirmwareDirectory(std::function<bool (const std::string &)> handler) const241 bool FirmwareHandler::ForEachFirmwareDirectory(
242         std::function<bool(const std::string&)> handler) const {
243     for (const std::string& firmware_directory : firmware_directories_) {
244         if (std::invoke(handler, firmware_directory)) {
245             return true;
246         }
247     }
248 
249     glob_t glob_result;
250     glob("/apex/*/etc/firmware/", GLOB_MARK, nullptr, &glob_result);
251     auto free_glob = android::base::make_scope_guard(std::bind(&globfree, &glob_result));
252     for (size_t i = 0; i < glob_result.gl_pathc; i++) {
253         char* apex_firmware_directory = glob_result.gl_pathv[i];
254         // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
255         // /apex/<name> paths, so unless we filter them out, we will look into the
256         // same apex twice.
257         if (strchr(apex_firmware_directory, '@')) {
258             continue;
259         }
260         if (std::invoke(handler, apex_firmware_directory)) {
261             return true;
262         }
263     }
264 
265     return false;
266 }
267 
HandleUevent(const Uevent & uevent)268 void FirmwareHandler::HandleUevent(const Uevent& uevent) {
269     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
270 
271     // Loading the firmware in a child means we can do that in parallel...
272     auto pid = fork();
273     if (pid == -1) {
274         PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
275     }
276     if (pid == 0) {
277         Timer t;
278         auto firmware = GetFirmwarePath(uevent);
279         ProcessFirmwareEvent(uevent.path, firmware);
280         LOG(INFO) << "loading " << uevent.path << " took " << t;
281         _exit(EXIT_SUCCESS);
282     }
283 }
284 
285 }  // namespace init
286 }  // namespace android
287