• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/sendfile.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 
24 #include <string>
25 #include <thread>
26 
27 #include <android-base/chrono_utils.h>
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/unique_fd.h>
31 
32 using android::base::Timer;
33 using android::base::unique_fd;
34 using android::base::WriteFully;
35 
36 namespace android {
37 namespace init {
38 
LoadFirmware(const Uevent & uevent,const std::string & root,int fw_fd,size_t fw_size,int loading_fd,int data_fd)39 static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size,
40                          int loading_fd, int data_fd) {
41     // Start transfer.
42     WriteFully(loading_fd, "1", 1);
43 
44     // Copy the firmware.
45     int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
46     if (rc == -1) {
47         PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware
48                     << "' }";
49     }
50 
51     // Tell the firmware whether to abort or commit.
52     const char* response = (rc != -1) ? "0" : "-1";
53     WriteFully(loading_fd, response, strlen(response));
54 }
55 
IsBooting()56 static bool IsBooting() {
57     return access("/dev/.booting", F_OK) == 0;
58 }
59 
ProcessFirmwareEvent(const Uevent & uevent)60 static void ProcessFirmwareEvent(const Uevent& uevent) {
61     int booting = IsBooting();
62 
63     LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'";
64 
65     std::string root = "/sys" + uevent.path;
66     std::string loading = root + "/loading";
67     std::string data = root + "/data";
68 
69     unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
70     if (loading_fd == -1) {
71         PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware;
72         return;
73     }
74 
75     unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
76     if (data_fd == -1) {
77         PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware;
78         return;
79     }
80 
81     static const char* firmware_dirs[] = {"/etc/firmware/", "/odm/firmware/",
82                                           "/vendor/firmware/", "/firmware/image/"};
83 
84 try_loading_again:
85     for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
86         std::string file = firmware_dirs[i] + uevent.firmware;
87         unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
88         struct stat sb;
89         if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
90             LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
91             return;
92         }
93     }
94 
95     if (booting) {
96         // If we're not fully booted, we may be missing
97         // filesystems needed for firmware, wait and retry.
98         std::this_thread::sleep_for(100ms);
99         booting = IsBooting();
100         goto try_loading_again;
101     }
102 
103     LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware;
104 
105     // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
106     write(loading_fd, "-1", 2);
107 }
108 
HandleFirmwareEvent(const Uevent & uevent)109 void HandleFirmwareEvent(const Uevent& uevent) {
110     if (uevent.subsystem != "firmware" || uevent.action != "add") return;
111 
112     // Loading the firmware in a child means we can do that in parallel...
113     auto pid = fork();
114     if (pid == -1) {
115         PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
116     }
117     if (pid == 0) {
118         Timer t;
119         ProcessFirmwareEvent(uevent);
120         LOG(INFO) << "loading " << uevent.path << " took " << t;
121         _exit(EXIT_SUCCESS);
122     }
123 }
124 
125 }  // namespace init
126 }  // namespace android
127