我正在 Ubuntu 下编译自定义内核,但遇到了内核似乎不知道在哪里查找固件的问题。在 Ubuntu 8.04 下,固件以与驱动程序模块相同的方式绑定(bind)到内核版本。例如,内核 2.6.24-24-generic 将其内核模块存储在:
/lib/modules/2.6.24-24-generic
及其固件:
/lib/firmware/2.6.24-24-generic
当我根据“Alternate Build Method: The Old-Fashioned Debian Way ”编译 2.6.24-24-generic Ubuntu 内核时,我得到了相应的模块目录,我的所有设备都可以工作,除了需要固件的设备,例如我的英特尔无线卡(ipw2200 模块)。
例如,内核日志显示,当 ipw2200 尝试加载固件时,控制固件加载的内核子系统无法找到它:
ipw2200: Detected Intel PRO/Wireless 2200BG Network Connection
ipw2200: ipw2200-bss.fw request_firmware failed: Reason -2
errno-base.h 将其定义为:
#define ENOENT 2 /* No such file or directory */
(返回 ENOENT 的函数在它前面放了一个减号。)
我尝试在/lib/firmware 中创建一个符号链接(symbolic link),其中我的内核名称指向 2.6.24-24-generic 目录,但这导致了同样的错误。该固件是非 GPL,由 Intel 提供并由 Ubuntu 打包。我不相信它与特定内核版本有任何实际联系。
cmp
说明各个目录下的版本是一样的。那么内核如何知道去哪里寻找固件呢?
更新
我发现 this solution 正是我遇到的问题,但是它不再有效,因为 Ubuntu 已经消除了
/etc/hotplug.d
并且不再将其固件存储在 /usr/lib/hotplug/firmware
中。更新 2
更多的研究发现了更多的答案。在
udev
版本 92 之前,程序 firmware_helper
是加载固件的方式。从 udev
93 开始,这个程序被一个名为 firmware.sh 的脚本所取代,据我所知,它提供了相同的功能。这两个都将固件路径硬编码为 /lib/firmware
。 Ubuntu 似乎仍在使用 /lib/udev/firmware_helper
二进制文件。固件文件的名称传递给环境变量
firmware_helper
中的 $FIRMWARE
,该变量连接到路径 /lib/firmware
并用于加载固件。加载固件的实际请求是由驱动程序(在我的例子中为 ipw2200)通过系统调用发出的:
request_firmware(..., "ipw2200-bss.fw", ...);
现在,在调用
request_firmware
和 firmware_helper
的驱动程序之间的某个位置查看 $FIRMWARE
环境变量,内核包名称被添加到固件名称之前。那么谁在做呢?
最佳答案
从内核的角度来看,参见 /usr/src/linux/Documentation/firmware_class/README :
kernel(driver): calls request_firmware(&fw_entry, $FIRMWARE, device) userspace: - /sys/class/firmware/xxx/{loading,data} appear. - hotplug gets called with a firmware identifier in $FIRMWARE and the usual hotplug environment. - hotplug: echo 1 > /sys/class/firmware/xxx/loading kernel: Discard any previous partial load. userspace: - hotplug: cat appropriate_firmware_image > \ /sys/class/firmware/xxx/data kernel: grows a buffer in PAGE_SIZE increments to hold the image as it comes in. userspace: - hotplug: echo 0 > /sys/class/firmware/xxx/loading kernel: request_firmware() returns and the driver has the firmware image in fw_entry->{data,size}. If something went wrong request_firmware() returns non-zero and fw_entry is set to NULL. kernel(driver): Driver code calls release_firmware(fw_entry) releasing the firmware image and any related resource.
The kernel doesn't actually load any firmware at all. It simply informs userspace, "I want a firmware by the name of xxx", and waits for userspace to pipe the firmware image back to the kernel.
Now, on Ubuntu 8.04,
$ grep firmware /etc/udev/rules.d/80-program.rules # Load firmware on demand SUBSYSTEM=="firmware", ACTION=="add", RUN+="firmware_helper"
so as you've discovered, udev
is configured to run firmware_helper
when the kernel asks for firmware.
$ apt-get source udev Reading package lists... Done Building dependency tree Reading state information... Done Need to get 312kB of source archives. Get:1 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (dsc) [716B] Get:2 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (tar) [245kB] Get:3 http://us.archive.ubuntu.com hardy-security/main udev 117-8ubuntu0.2 (diff) [65.7kB] Fetched 312kB in 1s (223kB/s) gpg: Signature made Tue 14 Apr 2009 05:31:34 PM EDT using DSA key ID 17063E6D gpg: Can't check signature: public key not found dpkg-source: extracting udev in udev-117 dpkg-source: unpacking udev_117.orig.tar.gz dpkg-source: applying ./udev_117-8ubuntu0.2.diff.gz $ cd udev-117/ $ cat debian/patches/80-extras-firmware.patch
If you read the source, you'll find that Ubuntu wrote a firmware_helper
which is hard-coded to first look for /lib/modules/$(uname -r)/$FIRMWARE
, then /lib/modules/$FIRMWARE
, and no other locations. Translating it to sh
, it does approximately this:
echo -n 1 > /sys/$DEVPATH/loading
cat /lib/firmware/$(uname -r)/$FIRMWARE > /sys/$DEVPATH/data \
|| cat /lib/firmware/$FIRMWARE > /sys/$DEVPATH/data
if [ $? = 0 ]; then
echo -n 1 > /sys/$DEVPATH/loading
echo -n -1 > /sys/$DEVPATH/loading
fi
这正是内核期望的格式。
长话短说:Ubuntu 的
udev
软件包具有自定义功能,它们总是首先在 /lib/firmware/$(uname -r)
中查找。此策略正在用户空间中处理。关于linux-kernel - Linux Kernel 如何知道在哪里寻找驱动程序固件?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/950107/