用户空间 (Android init/ueventd)内核 (firmware_loader)驱动程序用户空间 (Android init/ueventd)内核 (firmware_loader)驱动程序alt[找到 firmware][未找到]调用 request_firmware()查找 /lib/firmware返回 firmware触发 sysfs fallback发送 ueventinit/ueventd 接收 uevent根据 ueventd.rc 依次查找/vendor/firmware/firmware/image...通过 sysfs fallback 提供 firmware返回 firmware

本文将解析 Android 系统中固件加载的路径和流程,帮助理解驱动程序如何获取所需的固件文件。

固件的加载流程主要由上图所描述,下面我们结合代码来详细说明。

驱动程序调用

驱动程序在需要加载固件时,会调用 request_firmware() 系列函数,这些函数被定义在 Linux 内核的 include/linux/firmware.h 中,以下是高通 CNSS 平台驱动的示例代码:

https://github.com/WANG-Guangxin/wlan-driver/blob/5113495b16420b49004c444715d2daae2066e7dc/platform/cnss2/main.c#L4032

1
2
3
4
5
6
7
8
9
10
11
int cnss_request_firmware_direct(struct cnss_plat_data *plat_priv,
const struct firmware **fw_entry,
const char *filename)
{
if (IS_ENABLED(CONFIG_CNSS_REQ_FW_DIRECT))
return request_firmware_direct(fw_entry, filename,
&plat_priv->plat_dev->dev);
else
return firmware_request_nowarn(fw_entry, filename,
&plat_priv->plat_dev->dev);
}

内核处理

对于 CNSS 驱动可能使用到的 request_firmware_direct()firmware_request_nowarn() 函数,他们的实现位于内核的 drivers/base/firmware_loader/main.c 文件中。

request_firmware_direct

request_firmware_direct() 函数直接从文件系统加载固件,不会回退到用户空间的 udev 或者 Android 的 init 进程。

https://github.com/torvalds/linux/blob/260f6f4fda93c8485c8037865c941b42b9cba5d2/drivers/base/firmware_loader/main.c#L1002

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* request_firmware_direct() - load firmware directly without usermode helper
* @firmware_p: pointer to firmware image
* @name: name of firmware file
* @device: device for which firmware is being loaded
*
* This function works pretty much like request_firmware(), but this doesn't
* fall back to usermode helper even if the firmware couldn't be loaded
* directly from fs. Hence it's useful for loading optional firmwares, which
* aren't always present, without extra long timeouts of udev.
**/
int request_firmware_direct(const struct firmware **firmware_p,
const char *name, struct device *device)
{
int ret;

__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN |
FW_OPT_NOFALLBACK_SYSFS);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL_GPL(request_firmware_direct);

firmware_request_nowarn

firmware_request_nowarn() 函数则会尝试从文件系统加载固件,如果失败则会回退到 sysfs fallback 机制,但不会打印警告信息。所谓的 sysfs fallback 机制,就是通过发送 uevent 给用户空间的 init 进程,来查找其他可能的固件路径。

https://github.com/torvalds/linux/blob/260f6f4fda93c8485c8037865c941b42b9cba5d2/drivers/base/firmware_loader/main.c#L977

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* firmware_request_nowarn() - request for an optional fw module
* @firmware: pointer to firmware image
* @name: name of firmware file
* @device: device for which firmware is being loaded
*
* This function is similar in behaviour to request_firmware(), except it
* doesn't produce warning messages when the file is not found. The sysfs
* fallback mechanism is enabled if direct filesystem lookup fails. However,
* failures to find the firmware file with it are still suppressed. It is
* therefore up to the driver to check for the return value of this call and to
* decide when to inform the users of errors.
**/
int firmware_request_nowarn(const struct firmware **firmware, const char *name,
struct device *device)
{
int ret;

/* Need to pin this module until return */
__module_get(THIS_MODULE);
ret = _request_firmware(firmware, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL_GPL(firmware_request_nowarn);

firmware_fallback_sysfs

无论是 request_firmware_direct() 还是 firmware_request_nowarn(),它们都会调用 _request_firmware() 函数来处理固件加载的具体逻辑。

而在 _request_firmware() 中,我们可以看到这里存在很多 fallback 机制,这里我们重点关注的是 sysfs fallback 机制。

https://github.com/torvalds/linux/blob/260f6f4fda93c8485c8037865c941b42b9cba5d2/drivers/base/firmware_loader/main.c#L906

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* called from request_firmware() and request_firmware_work_func() */
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device, void *buf, size_t size,
size_t offset, u32 opt_flags)
{
struct firmware *fw = NULL;
struct cred *kern_cred = NULL;
const struct cred *old_cred;
bool nondirect = false;
int ret;

if (!firmware_p)
return -EINVAL;

if (!name || name[0] == '\0') {
ret = -EINVAL;
goto out;
}


/*
* Reject firmware file names with ".." path components.
* There are drivers that construct firmware file names from
* device-supplied strings, and we don't want some device to be
* able to tell us "I would like to be sent my firmware from
* ../../../etc/shadow, please".
*
* This intentionally only looks at the firmware name, not at
* the firmware base directory or at symlink contents.
*/
if (name_contains_dotdot(name)) {
dev_warn(device,
"Firmware load for '%s' refused, path contains '..' component\n",
name);
ret = -EINVAL;
goto out;
}

ret = _request_firmware_prepare(&fw, name, device, buf, size,
offset, opt_flags);
if (ret <= 0) /* error or already assigned */
goto out;

/*
* We are about to try to access the firmware file. Because we may have been
* called by a driver when serving an unrelated request from userland, we use
* the kernel credentials to read the file.
*/
kern_cred = prepare_kernel_cred(&init_task);
if (!kern_cred) {
ret = -ENOMEM;
goto out;
}
old_cred = override_creds(kern_cred);

ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);

/* Only full reads can support decompression, platform, and sysfs. */
if (!(opt_flags & FW_OPT_PARTIAL))
nondirect = true;

#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
if (ret == -ENOENT && nondirect)
ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
fw_decompress_zstd);
#endif
#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
if (ret == -ENOENT && nondirect)
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
fw_decompress_xz);
#endif
if (ret == -ENOENT && nondirect)
ret = firmware_fallback_platform(fw->priv);

if (ret) {
if (!(opt_flags & FW_OPT_NO_WARN))
dev_warn(device,
"Direct firmware load for %s failed with error %d\n",
name, ret);
if (nondirect)
ret = firmware_fallback_sysfs(fw, name, device,
opt_flags, ret);
} else
ret = assign_fw(fw, device);

revert_creds(old_cred);
put_cred(kern_cred);

out:
if (ret < 0) {
fw_abort_batch_reqs(fw);
release_firmware(fw);
fw = NULL;
} else {
fw_log_firmware_info(fw, name, device);
}

*firmware_p = fw;
return ret;
}

firmware_fallback_sysfs() 函数的实现定义在 drivers/base/firmware_loader/fallback.c 中,这个文件只有 200 多行代码,其中实现了向用户空间发送 uevent 的逻辑。

https://github.com/torvalds/linux/blob/260f6f4fda93c8485c8037865c941b42b9cba5d2/drivers/base/firmware_loader/fallback.c#L224

主要的函数调用过程如下:

firmware_fallback_sysfs => fw_load_from_user_helper => fw_load_sysfs_fallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism
* @fw_sysfs: firmware sysfs information for the firmware to load
* @timeout: timeout to wait for the load
*
* In charge of constructing a sysfs fallback interface for firmware loading.
**/
static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
{
int retval = 0;
struct device *f_dev = &fw_sysfs->dev;
struct fw_priv *fw_priv = fw_sysfs->fw_priv;

/* fall back on userspace loading */
if (!fw_priv->data)
fw_priv->is_paged_buf = true;

dev_set_uevent_suppress(f_dev, true);

retval = device_add(f_dev);
if (retval) {
dev_err(f_dev, "%s: device_register failed\n", __func__);
goto err_put_dev;
}

mutex_lock(&fw_lock);
if (fw_load_abort_all || fw_state_is_aborted(fw_priv)) {
mutex_unlock(&fw_lock);
retval = -EINTR;
goto out;
}
list_add(&fw_priv->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);

if (fw_priv->opt_flags & FW_OPT_UEVENT) {
fw_priv->need_uevent = true;
dev_set_uevent_suppress(f_dev, false);
dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
kobject_uevent(&fw_sysfs->dev.kobj, KOBJ_ADD); // 发送 uevent 通知
} else {
timeout = MAX_JIFFY_OFFSET;
}

retval = fw_sysfs_wait_timeout(fw_priv, timeout);
if (retval < 0 && retval != -ENOENT) {
mutex_lock(&fw_lock);
fw_load_abort(fw_sysfs);
mutex_unlock(&fw_lock);
}

if (fw_state_is_aborted(fw_priv)) {
if (retval == -ERESTARTSYS)
retval = -EINTR;
} else if (fw_priv->is_paged_buf && !fw_priv->data)
retval = -ENOMEM;

out:
device_del(f_dev);
err_put_dev:
put_device(f_dev);
return retval;
}

用户空间处理

firmware 路径的配置文件

在 Android 系统中,固件的加载路径通常由 ueventd.rc 文件配置。这个文件位于 /vendor/etc//system/etc/ 目录下。

在 AOSP 代码库中,ueventd.rc 文件的示例可以在以下路径找到:system/core/rootdir/ueventd.rc

其中 firmware_directories 指令用于指定固件的搜索路径。Android 的 init 进程会根据这些路径来查找固件文件。

注意: 中国大陆屏蔽了 Google 的代码托管服务,无法直接访问 cs.android.com 需要通过 VPN 或其他方式访问。

https://cs.android.com/android/platform/superproject/main/+/main:system/core/rootdir/ueventd.rc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import /vendor/etc/ueventd.rc
import /odm/etc/ueventd.rc

# firmware_directories 用于指定固件的搜索路径
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
uevent_socket_rcvbuf_size 16M

subsystem graphics
devname uevent_devpath
dirname /dev/graphics

subsystem drm
devname uevent_devpath
dirname /dev/dri

subsystem input
devname uevent_devpath
dirname /dev/input

subsystem sound
devname uevent_devpath
dirname /dev/snd

subsystem dma_heap
devname uevent_devpath
dirname /dev/dma_heap

subsystem vfio
devname uevent_devpath
dirname /dev/vfio

# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
#
# format for /dev rules: devname mode uid gid
# format for /sys rules: nodename attr mode uid gid
# shortcut: "mtd@NN" expands to "/dev/mtd/mtdNN"

/dev/null 0666 root root
/dev/zero 0666 root root
/dev/full 0666 root root
/dev/ptmx 0666 root root
/dev/tty 0666 root root
/dev/random 0666 root root
/dev/urandom 0666 root root
# Aside from kernel threads, only prng_seeder needs access to HW RNG
/dev/hw_random 0400 prng_seeder prng_seeder
/dev/ashmem* 0666 root root
/dev/binder 0666 root root
/dev/hwbinder 0666 root root
/dev/vndbinder 0666 root root
/dev/vfio/* 0666 root root

/dev/pmsg0 0222 root log
/dev/dma_heap/system 0444 system system
/dev/dma_heap/system-uncached 0444 system system
/dev/dma_heap/system-secure 0444 system system

# kms driver for drm based gpu
/dev/dri/* 0666 root graphics

# these should not be world writable
/dev/uhid 0660 uhid uhid
/dev/uinput 0660 uhid uhid
/dev/rtc0 0640 system system
/dev/tty0 0660 root system
/dev/graphics/* 0660 root graphics
/dev/input/* 0660 root input
/dev/v4l-touch* 0660 root input
/dev/snd/* 0660 system audio
/dev/bus/usb/* 0660 root usb
/dev/mtp_usb 0660 root mtp
/dev/usb_accessory 0660 root usb
/dev/tun 0660 system vpn
/dev/hidraw* 0660 system system

# CDMA radio interface MUX
/dev/ppp 0660 radio vpn

/dev/kvm 0666 root root
/dev/vhost-vsock 0666 root root

# sysfs properties
/sys/devices/platform/trusty.* trusty_version 0440 root log
/sys/devices/virtual/input/input* enable 0660 root input
/sys/devices/virtual/input/input* poll_delay 0660 root input
/sys/devices/virtual/usb_composite/* enable 0664 root system
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
/sys/devices/system/cpu/cpu* cpufreq/scaling_min_freq 0664 system system
/sys/devices/virtual/misc/uhid/*/leds/* brightness 0664 system system
/sys/devices/virtual/misc/uhid/*/leds/* multi_intensity 0664 system system

ueventd.rc 文件解析

ueventd.rc 文件中定义了多个固件搜索路径,这些路径会被 Android 的 init 进程加载。

system/core/init/main.cpp 中,会调用 ueventd_main() 函数来处理 uevent 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using namespace android::init;

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
__hwasan_set_error_report_callback(AsanReportCallback);
#endif
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv); // 处理 ueventd 的主函数
}
...
return FirstStageMain(argc, argv);
}

system/core/init/ueventd.cpp 中,ueventd_main() 函数中,会调用静态方法 GetConfiguration() 来读取和解析 ueventd.rc 文件。将解析到的内容保存在 ueventd_configuration 对象中。

然后通过 ueventd_configuration 中的成员变量,也就是 ueventd.rc 文件中定义的内容,来创建各种 uevent 处理器。

其中,就创建了用来处理固件加载的 FirmwareHandler 对象。

https://cs.android.com/android/platform/superproject/main/+/main:system/core/init/ueventd.cpp;l=337;drc=61197364367c9e404c7da6900658f1b16c42d0da;bpv=1;bpt=1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
umask(000);

android::base::InitLogging(argv, &android::base::KernelLogger);

LOG(INFO) << "ueventd started!";

SelinuxSetupKernelLogging();
SelabelInitialize();

// 维护一个 uevent 处理器的容器
std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;

// 获取 ueventd.rc 配置
auto ueventd_configuration = GetConfiguration();

UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);

// Right after making DeviceHandler, replay all events looking for which
// block device has the boot partition. This lets us make symlinks
// for all of the other partitions on the same disk. Note that by the time
// we get here we know that the boot partition has already shown up (if
// we're looking for it) so just regenerating events is enough to know
// we'll see it.
std::unique_ptr<DeviceHandler> device_handler = std::make_unique<DeviceHandler>(
std::move(ueventd_configuration.dev_permissions),
std::move(ueventd_configuration.sysfs_permissions),
std::move(ueventd_configuration.drivers), std::move(ueventd_configuration.subsystems),
android::fs_mgr::GetBootDevices(), android::fs_mgr::GetBootPartUuid(), true);
uevent_listener.RegenerateUevents([&](const Uevent& uevent) -> ListenerAction {
bool uuid_check_done = device_handler->CheckUeventForBootPartUuid(uevent);
return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
});

uevent_handlers.emplace_back(std::move(device_handler));

// 创建固件处理器,这里就传入了 ueventd_configuration 中的固件目录 firmware_directories
uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
std::move(ueventd_configuration.firmware_directories),
std::move(ueventd_configuration.external_firmware_handlers)));
...

GetConfiguration() 函数中我们可以看到有哪些 ueventd.rc 会被解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static UeventdConfiguration GetConfiguration() {
if (IsMicrodroid()) {
return ParseConfig({"/system/etc/ueventd.rc", "/vendor/etc/ueventd.rc"});
}

auto hardware = android::base::GetProperty("ro.hardware", "");

struct LegacyPathInfo {
std::string legacy_path;
std::string preferred;
};
std::vector<LegacyPathInfo> legacy_paths{
{"/vendor/ueventd.rc", "/vendor/etc/ueventd.rc"},
{"/odm/ueventd.rc", "/odm/etc/ueventd.rc"},
{"/ueventd." + hardware + ".rc", "another ueventd.rc file"}};

std::vector<std::string> canonical{"/system/etc/ueventd.rc"};

if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
// TODO: Remove these legacy paths once Android S is no longer supported.
for (const auto& info : legacy_paths) {
canonical.push_back(info.legacy_path);
}
} else {
// Warn if newer device is using legacy paths.
for (const auto& info : legacy_paths) {
if (access(info.legacy_path.c_str(), F_OK) == 0) {
LOG(FATAL_WITHOUT_ABORT)
<< "Legacy ueventd configuration file detected and will not be parsed: "
<< info.legacy_path << ". Please move your configuration to "
<< info.preferred << " instead.";
}
}
}

return ParseConfig(canonical);
}

FirmwareHandler

FirmwareHandler 类是处理固件加载的核心类,它继承自 UeventHandler,并实现了 HandleUevent() 方法来处理接收到的 uevent。

它的构造函数接收固件目录和外部固件处理器的列表,并将这些信息存储在成员变量 firmware_directories_ 中。

https://cs.android.com/android/platform/superproject/main/+/main:system/core/init/firmware_handler.h;l=45?q=FirmwareHandler&ss=android%2Fplatform%2Fsuperproject%2Fmain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class FirmwareHandler : public UeventHandler {
public:
FirmwareHandler(std::vector<std::string> firmware_directories,
std::vector<ExternalFirmwareHandler> external_firmware_handlers);
virtual ~FirmwareHandler() = default;

void HandleUevent(const Uevent& uevent) override;

private:
friend void FirmwareTestWithExternalHandler(const std::string& test_name,
bool expect_new_firmware);

std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& path, 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_;
};

HandleUevent() 方法中,FirmwareHandler 类会检查接收到的 uevent 是否与固件相关,并在需要时创建一个子进程来处理固件加载事件。

在子进程中,它会调用 GetFirmwarePath() 方法来获取固件的完整路径,这个 GetFirmwarePath() 方法实际上是根据 uevent 的信息中拿到固件名称。

然后在 ProcessFirmwareEvent() 方法中,会依次遍历 firmware_directories_ 中的路径,尝试加载固件文件。如果找到了固件文件,就会将其写入到 /sys/<path>/data 文件中,并将 /sys/<path>/loading 文件的内容设置为 0,表示固件加载完成。

如果没有找到固件文件,则会打印错误信息,并将 /sys/<path>/loading 文件的内容设置为 -1,表示加载失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
void FirmwareHandler::HandleUevent(const Uevent& uevent) {
if (uevent.subsystem != "firmware" || uevent.action != "add") return;

// Loading the firmware in a child means we can do that in parallel...
auto pid = fork();
if (pid == -1) {
PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
}
if (pid == 0) {
Timer t;
auto firmware = GetFirmwarePath(uevent);
ProcessFirmwareEvent(uevent.path, firmware);
LOG(INFO) << "loading " << uevent.path << " took " << t;
_exit(EXIT_SUCCESS);
}
}

static void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd,
size_t fw_size, int loading_fd, int data_fd) {
// Start transfer.
WriteFully(loading_fd, "1", 1);

// Copy the firmware.
int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
if (rc == -1) {
PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << firmware << "' }";
}

// Tell the firmware whether to abort or commit.
const char* response = (rc != -1) ? "0" : "-1";
WriteFully(loading_fd, response, strlen(response));
}

void FirmwareHandler::ProcessFirmwareEvent(const std::string& path,
const std::string& firmware) const {
std::string root = "/sys" + path;
std::string loading = root + "/loading";
std::string data = root + "/data";

unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));
if (loading_fd == -1) {
PLOG(ERROR) << "couldn't open firmware loading fd for " << firmware;
return;
}

unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));
if (data_fd == -1) {
PLOG(ERROR) << "couldn't open firmware data fd for " << firmware;
return;
}

std::vector<std::string> attempted_paths_and_errors;
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));
return false;
}
struct stat sb;
if (fstat(fw_fd.get(), &sb) == -1) {
attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
", fstat failed: " + strerror(errno));
return false;
}
LOG(INFO) << "found " << file << " for " << path;

// 这个 LoadFirmware 函数会将固件写入到 data_fd 中
LoadFirmware(firmware, root, fw_fd.get(), sb.st_size, loading_fd.get(), data_fd.get());
return true;
};

int booting = IsBooting();
try_loading_again:
attempted_paths_and_errors.clear();
if (ForEachFirmwareDirectory(TryLoadFirmware)) {
return;
}

if (booting) {
// If we're not fully booted, we may be missing
// filesystems needed for firmware, wait and retry.
std::this_thread::sleep_for(100ms);
booting = IsBooting();
goto try_loading_again;
}

LOG(ERROR) << "firmware: could not find firmware for " << firmware;
for (const auto& message : attempted_paths_and_errors) {
LOG(ERROR) << message;
}

// Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
write(loading_fd.get(), "-1", 2);
}