Linux 内核如何知道在哪里寻找驱动程序固件?
我正在 Ubuntu 下编译自定义内核,但遇到了我的内核似乎不知道在哪里寻找固件的问题。 在 Ubuntu 8.04 下,固件与内核版本相关联,就像驱动程序模块一样。 例如,内核 2.6.24-24-generic 将其内核模块存储在:
/lib/modules/2.6.24-24-generic
并将其固件存储在:
/lib/firmware/2.6.24-24-generic
当我根据“替代构建方法:老式 Debian 方式" 我获得了适当的模块目录,并且我的所有设备都可以工作,除了需要固件的设备(例如我的设备)英特尔无线网卡(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 中创建一个符号链接,其中我的内核名称指向 2.6.24-24-generic 目录,但这导致了相同的错误。 该固件是非 GPL,由 Intel 提供并由 Ubuntu 打包。 我不认为它与特定内核版本有任何实际联系。 cmp
表明各个目录中的版本是相同的。
那么内核如何知道在哪里寻找固件呢?
更新
我找到了这个解决方案来解决我所遇到的确切问题但是它不再起作用,因为 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
中的 firmware_helper
,该变量连接到路径 /lib/firmware
并用于加载固件。
加载固件的实际请求是由驱动程序(在我的例子中是 ipw2200)通过系统调用发出的:
request_firmware(..., "ipw2200-bss.fw", ...);
现在介于驱动程序调用 request_firmware
和 firmware_helper
之间,查看$FIRMWARE
环境变量,内核包名称被添加到固件名称前面。
那么是谁在做呢?
I'm compiling a custom kernel under Ubuntu and I'm running into the problem that my kernel doesn't seem to know where to look for firmware. Under Ubuntu 8.04, firmware is tied to kernel version the same way driver modules are. For example, kernel 2.6.24-24-generic stores its kernel modules in:
/lib/modules/2.6.24-24-generic
and its firmware in:
/lib/firmware/2.6.24-24-generic
When I compile the 2.6.24-24-generic Ubuntu kernel according the "Alternate Build Method: The Old-Fashioned Debian Way" I get the appropriate modules directory and all my devices work except those requiring firmware such as my Intel wireless card (ipw2200 module).
The kernel log shows for example that when ipw2200 tries to load the firmware the kernel subsystem controlling the loading of firmware is unable to locate it:
ipw2200: Detected Intel PRO/Wireless 2200BG Network Connection
ipw2200: ipw2200-bss.fw request_firmware failed: Reason -2
errno-base.h defines this as:
#define ENOENT 2 /* No such file or directory */
(The function returning ENOENT puts a minus in front of it.)
I tried creating a symlink in /lib/firmware where my kernel's name pointed to the 2.6.24-24-generic directory, however this resulted in the same error. This firmware is non-GPL, provided by Intel and packed by Ubuntu. I don't believe it has any actual tie to a particular kernel version. cmp
shows that the versions in the various directories are identical.
So how does the kernel know where to look for firmware?
Update
I found this solution to the exact problem I'm having, however it no longer works as Ubuntu has eliminated /etc/hotplug.d
and no longer stores its firmware in /usr/lib/hotplug/firmware
.
Update2
Some more research turned up some more answers. Up until version 92 of udev
, the program firmware_helper
was the way firmware got loaded. Starting with udev
93 this program was replaced with a script named firmware.sh providing identical functionality as far as I can tell. Both of these hardcode the firmware path to /lib/firmware
. Ubuntu still seems to be using the /lib/udev/firmware_helper
binary.
The name of the firmware file is passed to firmware_helper
in the environment variable $FIRMWARE
which is concatenated to the path /lib/firmware
and used to load the firmware.
The actual request to load the firmware is made by the driver (ipw2200 in my case) via the system call:
request_firmware(..., "ipw2200-bss.fw", ...);
Now somewhere in between the driver calling request_firmware
and firmware_helper
looking at the $FIRMWARE
environment variable, the kernel package name is getting prepended to the firmware name.
So who's doing it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
从内核的角度来看,请参阅 /usr/src/linux/Documentation/firmware_class/README:
内核实际上根本不加载任何固件。 它只是通知用户空间,“我想要一个名为 xxx 的固件”,并等待用户空间将固件映像通过管道传输回内核。
现在,在 Ubuntu 8.04 上,
正如您所发现的,
udev
配置为在内核请求固件时运行firmware_helper
。如果您阅读源代码,您会发现 Ubuntu 编写了一个
firmware_helper
,它被硬编码为首先查找/lib/modules/$(uname -r)/$FIRMWARE,然后是
/lib/modules/$FIRMWARE
,没有其他位置。 将其翻译为sh
,它的作用大致如下:这正是内核期望的格式。
长话短说:Ubuntu 的
udev
软件包具有始终首先在/lib/firmware/$(uname -r)
中查找的自定义项。 该策略是在用户空间中处理的。From the kernel's perspective, see /usr/src/linux/Documentation/firmware_class/README:
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,
so as you've discovered,
udev
is configured to runfirmware_helper
when the kernel asks for firmware.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 tosh
, it does approximately this:which is exactly the format the kernel expects.
To make a long story short: Ubuntu's
udev
package has customizations that always look in/lib/firmware/$(uname -r)
first. This policy is being handled in userspace.哇,这是非常有用的信息,它让我在为需要固件的设备制作自定义 USB 内核模块时找到了问题的解决方案。
基本上,每个 Ubuntu 都带来了 hal、sysfs、devfs、udev 等的新版本……而且事情只是发生了变化。 事实上,我读到他们停止使用 hal。
因此,让我们再次对其进行逆向工程,使其与最新的 [Ubuntu] 系统相关。
在 Ubuntu Lucid(撰写本文时最新版本)上,使用
/lib/udev/rules.d/50-firmware.rules
。 该文件调用二进制文件/lib/udev/firmware
,神奇的事情就发生在这里。列表:/lib/udev/rules.d/50-firmware.rules
魔法应该是这样的(来源:Linux 设备驱动程序,第 3 版,第 14 章:Linux 设备模型):
加载
数据
loading
并停止固件加载过程loading
(向内核发出信号)如果您查看 Lucid 的 udev 源代码页面,位于 udev-151/extras/firmware/firmware.c 中,即该固件 /lib/udev/firmware 二进制文件的源代码,那么您就会发现 在。
摘录:Lucid 来源,udev-151/extras/firmware/firmware.c
此外,许多设备使用 Intel HEX 格式(包含校验和和其他内容的文本文件)(wiki 它我没有声誉,也无法链接)。 内核程序 ihex2fw(从 kernel_source/lib/firmware 中的 .HEX 文件上的 Makefile 调用)将这些 HEX 文件转换为任意设计的二进制格式,然后 Linux 内核通过
request_ihex_firmware
获取该格式,因为他们认为在内核中读取文本文件是愚蠢的(它会减慢速度)。Wow this is very useful information and it led me to the solution for my problem when making a custom USB kernel module for a device requiring firmware.
Basically, every Ubuntu brings a new rehash of hal,sysfs,devfs,udev,and so on...and things just change. In fact I read they stopped using hal.
So let's reverse engineer this yet again so it's pertinent to the latest [Ubuntu] systems.
On Ubuntu Lucid (the latest at time of writing),
/lib/udev/rules.d/50-firmware.rules
is used. This file calls the binary/lib/udev/firmware
, where magic happens.Listing: /lib/udev/rules.d/50-firmware.rules
The magic should be something along these lines (source: Linux Device Drivers, 3rd Ed., Ch. 14: The Linux Device Model):
loading
data
loading
and halt firmware loading processloading
(signal the kernel)If you look at Lucid's source page for udev, in
udev-151/extras/firmware/firmware.c
, the source for that firmware /lib/udev/firmware binary, that's exactly what goes on.Excerpt: Lucid source, udev-151/extras/firmware/firmware.c
Additionally, many devices use an Intel HEX format (textish files containing checksum and other stuff) (wiki it i have no reputation and no ability to link). The kernel program ihex2fw (called from Makefile in kernel_source/lib/firmware on .HEX files) converts these HEX files to an arbitrary-designed binary format that the Linux kernel then picks up with
request_ihex_firmware
, because they thought reading text files in the kernel was silly (it would slow things down).在当前的 Linux 系统上,这是通过
udev
和firmware.agent
处理的。On current Linux systems, this is handled via
udev
and thefirmware.agent
.Linux 3.5.7 Gentoo,我也有同样的问题。
解决:
然后转到
设备驱动程序上的 /usr/src/linux ,删除所有不需要的无线驱动程序,将 Intell 2200 设置为模块并重新编译。
Linux 3.5.7 Gentoo, I have the same issue.
SOLVED:
Then go to /usr/src/linux
on device driver, remove all wirless drivers don't needed, set Intell 2200 as module and recompile.